1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Merge branch 'main' into requires-python

This commit is contained in:
Tzu-ping Chung 2022-03-10 15:41:05 +08:00 committed by GitHub
commit 2e5e9e54bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
527 changed files with 58974 additions and 14313 deletions

View file

@ -1,3 +0,0 @@
* pip version:
* Python version:
* Operating system:

View file

@ -6,3 +6,6 @@ contact_links:
- name: "💬 IRC: #pypa"
url: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
about: Chat with devs
- name: "(maintainers only) Blank issue"
url: https://github.com/pypa/pip/issues/new
about: For maintainers only.

8
.github/lock.yml vendored
View file

@ -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

View file

@ -11,7 +11,26 @@ on:
schedule:
- cron: 0 0 * * MON # Run every Monday at 00:00 UTC
env:
# The "FORCE_COLOR" variable, when set to 1,
# tells Nox to colorize itself.
FORCE_COLOR: "1"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
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:
@ -28,7 +47,7 @@ jobs:
- "src/pip/_vendor/**"
- "pyproject.toml"
tests:
# Anything that's touching testable stuff
# Anything that's touching code-related stuff
- ".github/workflows/ci.yml"
- "src/**"
- "tests/**"
@ -74,8 +93,8 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: pip install vendoring
- run: vendoring sync . --verbose
- run: pip install nox
- run: nox -s vendoring
- run: git diff --exit-code
tests-unix:
@ -92,11 +111,10 @@ jobs:
matrix:
os: [Ubuntu, MacOS]
python:
- 3.6
- 3.7
- 3.8
- 3.9
- "3.10.0-alpha - 3.10"
- "3.10"
steps:
- uses: actions/checkout@v2
@ -112,17 +130,17 @@ jobs:
if: matrix.os == 'MacOS'
run: brew install bzr
- run: pip install tox 'virtualenv<20'
- run: pip install nox 'virtualenv<20' 'setuptools != 60.6.0'
# Main check
- name: Run unit tests
run: >-
tox -e py --
nox -s test-${{ matrix.python }} --
-m unit
--verbose --numprocesses auto --showlocals
- name: Run integration tests
run: >-
tox -e py --
nox -s test-${{ matrix.python }} --
-m integration
--verbose --numprocesses auto --showlocals
--durations=5
@ -141,12 +159,11 @@ jobs:
matrix:
os: [Windows]
python:
- 3.6
- 3.7
# Commented out, since Windows tests are expensively slow.
# - 3.7
# - 3.8
- 3.9
- "3.10.0-alpha - 3.10"
# - 3.9
- "3.10"
group: [1, 2]
steps:
@ -170,7 +187,7 @@ jobs:
$acl.AddAccessRule($rule)
Set-Acl "R:\Temp" $acl
- run: pip install tox 'virtualenv<20'
- run: pip install nox 'virtualenv<20'
env:
TEMP: "R:\\Temp"
@ -178,7 +195,7 @@ jobs:
- name: Run unit tests
if: matrix.group == 1
run: >-
tox -e py --
nox -s test-${{ matrix.python }} --
-m unit
--verbose --numprocesses auto --showlocals
env:
@ -187,7 +204,7 @@ jobs:
- name: Run integration tests (group 1)
if: matrix.group == 1
run: >-
tox -e py --
nox -s test-${{ matrix.python }} --
-m integration -k "not test_install"
--verbose --numprocesses auto --showlocals
env:
@ -196,7 +213,7 @@ jobs:
- name: Run integration tests (group 2)
if: matrix.group == 2
run: >-
tox -e py --
nox -s test-${{ matrix.python }} --
-m integration -k "test_install"
--verbose --numprocesses auto --showlocals
env:

23
.github/workflows/lock-threads.yml vendored Normal file
View file

@ -0,0 +1,23 @@
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:
if: github.repository_owner == 'pypa'
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
with:
issue-inactive-days: '30'
pr-inactive-days: '15'

View file

@ -19,6 +19,7 @@ Dongweiming <dongweiming@admaster.com.cn> <ciici1234@hotmail.c
Dustin Ingram <di@di.codes> <di@users.noreply.github.com>
Endoh Takanao <djmchl@gmail.com>
Erik M. Bray <embray@stsci.edu>
Ee Durbin <ewdurbin@gmail.com>
Gabriel de Perthuis <g2p.code@gmail.com>
Hsiaoming Yang <lepture@me.com>
Hugo van Kemenade <hugovk@users.noreply.github.com> Hugo <hugovk@users.noreply.github.com>
@ -33,6 +34,7 @@ Ludovic Gasc <gmludo@gmail.com> <git@gmludo.eu>
Markus Hametner <fin+github@xbhd.org>
Masklinn <bitbucket.org@masklinn.net>
Matthew Iversen <teh.ivo@gmail.com> <teh.ivo@gmail.com>
Ofek Lev <ofekmeister@gmail.com>
Pi Delport <pjdelport@gmail.com>
<pnasrat@gmail.com> <pnasrat@googlemail.com>
Pradyun Gedam <pradyunsg@gmail.com> <pradyunsg@users.noreply.github.com>

View file

@ -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',

View file

@ -18,6 +18,7 @@ Alan Yee
Albert Tugushev
Albert-Guan
albertg
Alberto Sottile
Aleks Bunin
Alethea Flowers
Alex Gaynor
@ -77,6 +78,7 @@ Bastian Venthur
Ben Bodenmiller
Ben Darnell
Ben Hoyt
Ben Mares
Ben Rosser
Bence Nagy
Benjamin Peterson
@ -123,6 +125,7 @@ Chris Brinker
Chris Hunt
Chris Jerdonek
Chris McDonough
Chris Pawley
Chris Wolfe
Christian Clauss
Christian Heimes
@ -148,10 +151,13 @@ Cristina Muñoz
Curtis Doty
cytolentino
Daan De Meyer
Damian
Damian Quiroga
Damian Shaw
Dan Black
Dan Savilonis
Dan Sully
Dane Hillard
daniel
Daniel Collins
Daniel Hahler
@ -192,6 +198,7 @@ DiegoCaraballo
Dimitri Merejkowsky
Dirk Stolle
Dmitry Gladkov
Dmitry Volodin
Domen Kožar
Dominic Davis-Foster
Donald Stufft
@ -201,6 +208,7 @@ DrFeathers
Dustin Ingram
Dwayne Bailey
Ed Morley
Ee Durbin
Eitan Adler
ekristina
elainechan
@ -219,8 +227,6 @@ Eric Hanchrow
Eric Hopper
Erik M. Bray
Erik Rose
Ernest W Durbin III
Ernest W. Durbin III
Erwin Janssen
Eugene Vereshchagin
everdimension
@ -246,6 +252,7 @@ gizmoguy1
gkdoc
Gopinath M
GOTO Hayato
gousaiyang
gpiks
Greg Roodt
Greg Ward
@ -288,6 +295,7 @@ Jakub Wilk
James Cleveland
James Curtin
James Firth
James Gerity
James Polley
Jan Pokorný
Jannis Leidel
@ -301,6 +309,7 @@ Jelmer Vernooij
jenix21
Jeremy Stanley
Jeremy Zafran
Jesse Rittner
Jiashuo Li
Jim Fisher
Jim Garrison
@ -355,6 +364,7 @@ Laurent Bristiel
Laurent LAPORTE
Laurie O
Laurie Opperman
layday
Leon Sasson
Lev Givon
Lincoln de Sousa
@ -362,6 +372,7 @@ Lipis
Loren Carvalho
Lucas Cimon
Ludovic Gasc
Lukas Juhrich
Luke Macken
Luo Jiebin
luojiebin
@ -374,6 +385,7 @@ Mariatta
Mark Kohler
Mark Williams
Markus Hametner
Martey Dodoo
Martin Häcker
Martin Pavlasek
Masaki
@ -381,6 +393,7 @@ Masklinn
Matej Stuchlik
Mathew Jennings
Mathieu Bridon
Matt Bacchi
Matt Good
Matt Maker
Matt Robenolt
@ -392,6 +405,7 @@ Matthew Trumbell
Matthew Willson
Matthias Bussonnier
mattip
Maurits van Rees
Max W Chase
Maxim Kurnikov
Maxime Rouyrre
@ -406,6 +420,7 @@ Michael E. Karpeles
Michael Klich
Michael Williamson
michaelpacer
Michał Górny
Mickaël Schoentgen
Miguel Araujo Perez
Mihir Singh
@ -417,7 +432,10 @@ Miro Hrončok
Monica Baluna
montefra
Monty Taylor
Nadav Wexler
Nate Coraor
Nate Prewitt
Nathan Houghton
Nathaniel J. Smith
Nehal J Wani
Neil Botelho
@ -431,14 +449,16 @@ Nicole Harris
Nikhil Benesch
Nikita Chepanov
Nikolay Korolev
Nipunn Koorapati
Nitesh Sharma
Niyas Sait
Noah
Noah Gorny
Nowell Strite
NtaleGrey
nvdv
OBITORASU
Ofekmeister
Ofek Lev
ofrinevo
Oliver Jeeves
Oliver Mannion
@ -464,9 +484,12 @@ Paul Nasrat
Paul Oswald
Paul van der Linden
Paulus Schoutsen
Pavel Safronov
Pavithra Eswaramoorthy
Pawel Jasinski
Paweł Szramowski
Pekka Klärck
Peter Gessler
Peter Lisák
Peter Waller
petr-tik
@ -491,6 +514,7 @@ Preet Thakkar
Preston Holmes
Przemek Wrzos
Pulkit Goyal
q0w
Qiangning Hong
Quentin Lee
Quentin Pradet
@ -595,6 +619,7 @@ toonarmycaptain
Toshio Kuratomi
toxinu
Travis Swicegood
Tushar Sadhwani
Tzu-ping Chung
Valentin Haenel
Victor Stinner
@ -629,6 +654,7 @@ Yu Jian
Yuan Jing Vincent Yan
Zearin
Zhiping Deng
ziebam
Zvezdan Petkovic
Łukasz Langa
Семён Марьясин

View file

@ -1,4 +1,4 @@
Copyright (c) 2008-2021 The pip developers (see AUTHORS.txt file)
Copyright (c) 2008-present The pip developers (see AUTHORS.txt file)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View file

@ -6,6 +6,7 @@ include pyproject.toml
include src/pip/_vendor/README.rst
include src/pip/_vendor/vendor.txt
include src/pip/_vendor/pyparsing/diagram/template.jinja2
recursive-include src/pip/_vendor *LICENSE*
recursive-include src/pip/_vendor *COPYING*

216
NEWS.rst
View file

@ -7,9 +7,223 @@
To add a new change log entry, please see
https://pip.pypa.io/en/latest/development/contributing/#news-entries
.. towncrier release notes start
22.0.4 (2022-03-06)
===================
Deprecations and Removals
-------------------------
- Drop the doctype check, that presented a warning for index pages that use non-compliant HTML 5. (`#10903 <https://github.com/pypa/pip/issues/10903>`_)
Vendored Libraries
------------------
- Downgrade distlib to 0.3.3.
22.0.3 (2022-02-03)
===================
Features
--------
- Print the exception via ``rich.traceback``, when running with ``--debug``. (`#10791 <https://github.com/pypa/pip/issues/10791>`_)
Bug Fixes
---------
- Only calculate topological installation order, for packages that are going to be installed/upgraded.
This fixes an `AssertionError` that occured when determining installation order, for a very specific combination of upgrading-already-installed-package + change of dependencies + fetching some packages from a package index. This combination was especially common in Read the Docs' builds. (`#10851 <https://github.com/pypa/pip/issues/10851>`_)
- Use ``html.parser`` by default, instead of falling back to ``html5lib`` when ``--use-deprecated=html5lib`` is not passed. (`#10869 <https://github.com/pypa/pip/issues/10869>`_)
Improved Documentation
----------------------
- Clarify that using per-requirement overrides disables the usage of wheels. (`#9674 <https://github.com/pypa/pip/issues/9674>`_)
22.0.2 (2022-01-30)
===================
Deprecations and Removals
-------------------------
- Instead of failing on index pages that use non-compliant HTML 5, print a deprecation warning and fall back to ``html5lib``-based parsing for now. This simplifies the migration for non-compliant index pages, by letting such indexes function with a warning. (`#10847 <https://github.com/pypa/pip/issues/10847>`_)
22.0.1 (2022-01-30)
===================
Bug Fixes
---------
- Accept lowercase ``<!doctype html>`` on index pages. (`#10844 <https://github.com/pypa/pip/issues/10844>`_)
- Properly handle links parsed by html5lib, when using ``--use-deprecated=html5lib``. (`#10846 <https://github.com/pypa/pip/issues/10846>`_)
22.0 (2022-01-29)
=================
Process
-------
- Completely replace :pypi:`tox` in our development workflow, with :pypi:`nox`.
Deprecations and Removals
-------------------------
- Deprecate alternative progress bar styles, leaving only ``on`` and ``off`` as available choices. (`#10462 <https://github.com/pypa/pip/issues/10462>`_)
- Drop support for Python 3.6. (`#10641 <https://github.com/pypa/pip/issues/10641>`_)
- Disable location mismatch warnings on Python versions prior to 3.10.
These warnings were helping identify potential issues as part of the sysconfig -> distutils transition, and we no longer need to rely on reports from older Python versions for information on the transition. (`#10840 <https://github.com/pypa/pip/issues/10840>`_)
Features
--------
- Changed ``PackageFinder`` to parse HTML documents using the stdlib :class:`html.parser.HTMLParser` class instead of the ``html5lib`` package.
For now, the deprecated ``html5lib`` code remains and can be used with the ``--use-deprecated=html5lib`` command line option. However, it will be removed in a future pip release. (`#10291 <https://github.com/pypa/pip/issues/10291>`_)
- Utilise ``rich`` for presenting pip's default download progress bar. (`#10462 <https://github.com/pypa/pip/issues/10462>`_)
- Present a better error message when an invalid wheel file is encountered, providing more context where the invalid wheel file is. (`#10535 <https://github.com/pypa/pip/issues/10535>`_)
- Documents the ``--require-virtualenv`` flag for ``pip install``. (`#10588 <https://github.com/pypa/pip/issues/10588>`_)
- ``pip install <tab>`` autocompletes paths. (`#10646 <https://github.com/pypa/pip/issues/10646>`_)
- Allow Python distributors to opt-out from or opt-in to the ``sysconfig`` installation scheme backend by setting ``sysconfig._PIP_USE_SYSCONFIG`` to ``True`` or ``False``. (`#10647 <https://github.com/pypa/pip/issues/10647>`_)
- Make it possible to deselect tests requiring cryptography package on systems where it cannot be installed. (`#10686 <https://github.com/pypa/pip/issues/10686>`_)
- Start using Rich for presenting error messages in a consistent format. (`#10703 <https://github.com/pypa/pip/issues/10703>`_)
- Improve presentation of errors from subprocesses. (`#10705 <https://github.com/pypa/pip/issues/10705>`_)
- Forward pip's verbosity configuration to VCS tools to control their output accordingly. (`#8819 <https://github.com/pypa/pip/issues/8819>`_)
Bug Fixes
---------
- Optimize installation order calculation to improve performance when installing requirements that form a complex dependency graph with a large amount of edges. (`#10557 <https://github.com/pypa/pip/issues/10557>`_)
- When a package is requested by the user for upgrade, correctly identify that the extra-ed variant of that same package depended by another user-requested package is requesting the same package, and upgrade it accordingly. (`#10613 <https://github.com/pypa/pip/issues/10613>`_)
- Prevent pip from installing yanked releases unless explicitly pinned via the ``==`` or ``===`` operators. (`#10617 <https://github.com/pypa/pip/issues/10617>`_)
- Stop backtracking on build failures, by instead surfacing them to the user and aborting immediately. This behaviour provides more immediate feedback when a package cannot be built due to missing build dependencies or platform incompatibility. (`#10655 <https://github.com/pypa/pip/issues/10655>`_)
- Silence ``Value for <location> does not match`` warning caused by an erroneous patch in Slackware-distributed Python 3.9. (`#10668 <https://github.com/pypa/pip/issues/10668>`_)
- Fix an issue where pip did not consider dependencies with and without extras to be equal (`#9644 <https://github.com/pypa/pip/issues/9644>`_)
Vendored Libraries
------------------
- Upgrade CacheControl to 0.12.10
- Upgrade certifi to 2021.10.8
- Upgrade distlib to 0.3.4
- Upgrade idna to 3.3
- Upgrade msgpack to 1.0.3
- Upgrade packaging to 21.3
- Upgrade platformdirs to 2.4.1
- Add pygments 2.11.2 as a vendored dependency.
- Tree-trim unused portions of vendored pygments, to reduce the distribution size.
- Upgrade pyparsing to 3.0.7
- Upgrade Requests to 2.27.1
- Upgrade resolvelib to 0.8.1
- Add rich 11.0.0 as a vendored dependency.
- Tree-trim unused portions of vendored rich, to reduce the distribution size.
- Add typing_extensions 4.0.1 as a vendored dependency.
- Upgrade urllib3 to 1.26.8
21.3.1 (2021-10-22)
===================
Bug Fixes
---------
- Always refuse installing or building projects that have no ``pyproject.toml`` nor
``setup.py``. (`#10531 <https://github.com/pypa/pip/issues/10531>`_)
- Tweak running-as-root detection, to check ``os.getuid`` if it exists, on Unix-y and non-Linux/non-MacOS machines. (`#10565 <https://github.com/pypa/pip/issues/10565>`_)
- When installing projects with a ``pyproject.toml`` in editable mode, and the build
backend does not support :pep:`660`, prepare metadata using
``prepare_metadata_for_build_wheel`` instead of ``setup.py egg_info``. Also, refuse
installing projects that only have a ``setup.cfg`` and no ``setup.py`` nor
``pyproject.toml``. These restore the pre-21.3 behaviour. (`#10573 <https://github.com/pypa/pip/issues/10573>`_)
- Restore compatibility of where configuration files are loaded from on MacOS (back to ``Library/Application Support/pip``, instead of ``Preferences/pip``). (`#10585 <https://github.com/pypa/pip/issues/10585>`_)
Vendored Libraries
------------------
- Upgrade pep517 to 0.12.0
21.3 (2021-10-11)
=================
Deprecations and Removals
-------------------------
- Improve deprecation warning regarding the copying of source trees when installing from a local directory. (`#10128 <https://github.com/pypa/pip/issues/10128>`_)
- Suppress location mismatch warnings when pip is invoked from a Python source
tree, so ``ensurepip`` does not emit warnings on CPython ``make install``. (`#10270 <https://github.com/pypa/pip/issues/10270>`_)
- On Python 3.10 or later, the installation scheme backend has been changed to use
``sysconfig``. This is to anticipate the deprecation of ``distutils`` in Python
3.10, and its scheduled removal in 3.12. For compatibility considerations, pip
installations running on Python 3.9 or lower will continue to use ``distutils``. (`#10358 <https://github.com/pypa/pip/issues/10358>`_)
- Remove the ``--build-dir`` option and aliases, one last time. (`#10485 <https://github.com/pypa/pip/issues/10485>`_)
- In-tree builds are now the default. ``--use-feature=in-tree-build`` is now
ignored. ``--use-deprecated=out-of-tree-build`` may be used temporarily to ease
the transition. (`#10495 <https://github.com/pypa/pip/issues/10495>`_)
- Un-deprecate source distribution re-installation behaviour. (`#8711 <https://github.com/pypa/pip/issues/8711>`_)
Features
--------
- Replace vendored appdirs with platformdirs. (`#10202 <https://github.com/pypa/pip/issues/10202>`_)
- 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. (`#10249 <https://github.com/pypa/pip/issues/10249>`_)
- ``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. (`#10410 <https://github.com/pypa/pip/issues/10410>`_)
- ``pip show`` now sorts ``Requires`` and ``Required-By`` alphabetically. (`#10422 <https://github.com/pypa/pip/issues/10422>`_)
- Do not raise error when there are no files to remove with ``pip cache purge/remove``.
Instead log a warning and continue (to log that we removed 0 files). (`#10459 <https://github.com/pypa/pip/issues/10459>`_)
- When backtracking during dependency resolution, prefer the dependencies which are involved in the most recent conflict. This can significantly reduce the amount of backtracking required. (`#10479 <https://github.com/pypa/pip/issues/10479>`_)
- Cache requirement objects, to improve performance reducing reparses of requirement strings. (`#10550 <https://github.com/pypa/pip/issues/10550>`_)
- Support editable installs for projects that have a ``pyproject.toml`` and use a
build backend that supports :pep:`660`. (`#8212 <https://github.com/pypa/pip/issues/8212>`_)
- When a revision is specified in a Git URL, use git's partial clone feature to speed up source retrieval. (`#9086 <https://github.com/pypa/pip/issues/9086>`_)
- 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. (`#9349 <https://github.com/pypa/pip/issues/9349>`_)
- 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. (`#9498 <https://github.com/pypa/pip/issues/9498>`_)
Bug Fixes
---------
- Present a better error message, when a ``file:`` URL is not found. (`#10263 <https://github.com/pypa/pip/issues/10263>`_)
- 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. (`#10269 <https://github.com/pypa/pip/issues/10269>`_)
- Fix double unescape of HTML ``data-requires-python`` and ``data-yanked`` attributes. (`#10378 <https://github.com/pypa/pip/issues/10378>`_)
- New resolver: Fixes depth ordering of packages during resolution, e.g. a dependency 2 levels deep will be ordered before a dependency 3 levels deep. (`#10482 <https://github.com/pypa/pip/issues/10482>`_)
- Correctly indent metadata preparation messages in pip output. (`#10524 <https://github.com/pypa/pip/issues/10524>`_)
Vendored Libraries
------------------
- Remove appdirs as a vendored dependency.
- Upgrade distlib to 0.3.3
- Upgrade distro to 1.6.0
- Patch pkg_resources to use platformdirs rather than appdirs.
- Add platformdirs as a vendored dependency.
- Upgrade progress to 1.6
- Upgrade resolvelib to 0.8.0
- Upgrade urllib3 to 1.26.7
Improved Documentation
----------------------
- Update links of setuptools as setuptools moved these documents. The Simple Repository link now points to PyPUG as that is the canonical place of packaging specification, and setuptools's ``easy_install`` is deprecated. (`#10430 <https://github.com/pypa/pip/issues/10430>`_)
- Create a "Build System Interface" reference section, for documenting how pip interacts with build systems. (`#10497 <https://github.com/pypa/pip/issues/10497>`_)
21.2.4 (2021-08-12)
===================

View file

@ -74,178 +74,11 @@ when decision is needed.
*(a)abort*
Abort pip and return non-zero exit status.
.. _`build-interface`:
Build System Interface
======================
pip builds packages by invoking the build system. By default, builds will use
``setuptools``, but if a project specifies a different build system using a
``pyproject.toml`` file, as per :pep:`517`, pip will use that instead. As well
as package building, the build system is also invoked to install packages
direct from source. This is handled by invoking the build system to build a
wheel, and then installing from that wheel. The built wheel is cached locally
by pip to avoid repeated identical builds.
The current interface to the build system is via the ``setup.py`` command line
script - all build actions are defined in terms of the specific ``setup.py``
command line that will be run to invoke the required action.
Setuptools Injection
~~~~~~~~~~~~~~~~~~~~
When :pep:`517` is not used, the supported build system is ``setuptools``.
However, not all packages use ``setuptools`` in their build scripts. To support
projects that use "pure ``distutils``", pip injects ``setuptools`` into
``sys.modules`` before invoking ``setup.py``. The injection should be
transparent to ``distutils``-based projects, but 3rd party build tools wishing
to provide a ``setup.py`` emulating the commands pip requires may need to be
aware that it takes place.
Projects using :pep:`517` *must* explicitly use setuptools - pip does not do
the above injection process in this case.
Build System Output
~~~~~~~~~~~~~~~~~~~
Any output produced by the build system will be read by pip (for display to the
user if requested). In order to correctly read the build system output, pip
requires that the output is written in a well-defined encoding, specifically
the encoding the user has configured for text output (which can be obtained in
Python using ``locale.getpreferredencoding``). If the configured encoding is
ASCII, pip assumes UTF-8 (to account for the behaviour of some Unix systems).
Build systems should ensure that any tools they invoke (compilers, etc) produce
output in the correct encoding. In practice - and in particular on Windows,
where tools are inconsistent in their use of the "OEM" and "ANSI" codepages -
this may not always be possible. pip will therefore attempt to recover cleanly
if presented with incorrectly encoded build tool output, by translating
unexpected byte sequences to Python-style hexadecimal escape sequences
(``"\x80\xff"``, etc). However, it is still possible for output to be displayed
using an incorrect encoding (mojibake).
Under :pep:`517`, handling of build tool output is the backend's responsibility,
and pip simply displays the output produced by the backend. (Backends, however,
will likely still have to address the issues described above).
PEP 517 and 518 Support
~~~~~~~~~~~~~~~~~~~~~~~
As of version 10.0, pip supports projects declaring dependencies that are
required at install time using a ``pyproject.toml`` file, in the form described
in :pep:`518`. When building a project, pip will install the required
dependencies locally, and make them available to the build process.
Furthermore, from version 19.0 onwards, pip supports projects specifying the
build backend they use in ``pyproject.toml``, in the form described in
:pep:`517`.
When making build requirements available, pip does so in an *isolated
environment*. That is, pip does not install those requirements into the user's
``site-packages``, but rather installs them in a temporary directory which it
adds to the user's ``sys.path`` for the duration of the build. This ensures
that build requirements are handled independently of the user's runtime
environment. For example, a project that needs a recent version of setuptools
to build can still be installed, even if the user has an older version
installed (and without silently replacing that version).
In certain cases, projects (or redistributors) may have workflows that
explicitly manage the build environment. For such workflows, build isolation
can be problematic. If this is the case, pip provides a
``--no-build-isolation`` flag to disable build isolation. Users supplying this
flag are responsible for ensuring the build environment is managed
appropriately (including ensuring that all required build dependencies are
installed).
By default, pip will continue to use the legacy (direct ``setup.py`` execution
based) build processing for projects that do not have a ``pyproject.toml`` file.
Projects with a ``pyproject.toml`` file will use a :pep:`517` backend. Projects
with a ``pyproject.toml`` file, but which don't have a ``build-system`` section,
will be assumed to have the following backend settings::
[build-system]
requires = ["setuptools>=40.8.0", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"
.. note::
``setuptools`` 40.8.0 is the first version of setuptools that offers a
:pep:`517` backend that closely mimics directly executing ``setup.py``.
If a project has ``[build-system]``, but no ``build-backend``, pip will also use
``setuptools.build_meta:__legacy__``, but will expect the project requirements
to include ``setuptools`` and ``wheel`` (and will report an error if the
installed version of ``setuptools`` is not recent enough).
If a user wants to explicitly request :pep:`517` handling even though a project
doesn't have a ``pyproject.toml`` file, this can be done using the
``--use-pep517`` command line option. Similarly, to request legacy processing
even though ``pyproject.toml`` is present, the ``--no-use-pep517`` option is
available (although obviously it is an error to choose ``--no-use-pep517`` if
the project has no ``setup.py``, or explicitly requests a build backend). As
with other command line flags, pip recognises the ``PIP_USE_PEP517``
environment veriable and a ``use-pep517`` config file option (set to true or
false) to set this option globally. Note that overriding pip's choice of
whether to use :pep:`517` processing in this way does *not* affect whether pip
will use an isolated build environment (which is controlled via
``--no-build-isolation`` as noted above).
Except in the case noted above (projects with no :pep:`518` ``[build-system]``
section in ``pyproject.toml``), pip will never implicitly install a build
system. Projects **must** ensure that the correct build system is listed in
their ``requires`` list (this applies even if pip assumes that the
``setuptools`` backend is being used, as noted above).
.. _pep-518-limitations:
**Historical Limitations**:
* ``pip<18.0``: only supports installing build requirements from wheels, and
does not support the use of environment markers and extras (only version
specifiers are respected).
* ``pip<18.1``: build dependencies using .pth files are not properly supported;
as a result namespace packages do not work under Python 3.2 and earlier.
Future Developments
~~~~~~~~~~~~~~~~~~~
:pep:`426` notes that the intention is to add hooks to project metadata in
version 2.1 of the metadata spec, to explicitly define how to build a project
from its source. Once this version of the metadata spec is final, pip will
migrate to using that interface. At that point, the ``setup.py`` interface
documented here will be retained solely for legacy purposes, until projects
have migrated.
Specifically, applications should *not* expect to rely on there being any form
of backward compatibility guarantees around the ``setup.py`` interface.
Build Options
~~~~~~~~~~~~~
The ``--global-option`` and ``--build-option`` arguments to the ``pip install``
and ``pip wheel`` inject additional arguments into the ``setup.py`` command
(``--build-option`` is only available in ``pip wheel``). These arguments are
included in the command as follows:
.. tab:: Unix/macOS
.. code-block:: console
python setup.py <global_options> BUILD COMMAND <build_options>
.. tab:: Windows
.. code-block:: shell
py setup.py <global_options> BUILD COMMAND <build_options>
The options are passed unmodified, and presently offer direct access to the
distutils command line. Use of ``--global-option`` and ``--build-option``
should be considered as build system dependent, and may not be supported in the
current form if support for alternative build systems is added to pip.
This is now covered in :doc:`../reference/build-system/index`.
.. _`General Options`:

View file

@ -154,88 +154,15 @@ Requirements File Format
This section has been moved to :doc:`../reference/requirements-file-format`.
.. _`Requirement Specifiers`:
Requirement Specifiers
----------------------
pip supports installing from a package index using a :term:`requirement
specifier <pypug:Requirement Specifier>`. Generally speaking, a requirement
specifier is composed of a project name followed by optional :term:`version
specifiers <pypug:Version Specifier>`. :pep:`508` contains a full specification
of the format of a requirement. Since version 18.1 pip supports the
``url_req``-form specification.
Some examples:
::
SomeProject
SomeProject == 1.3
SomeProject >=1.2,<2.0
SomeProject[foo, bar]
SomeProject~=1.4.2
Since version 6.0, pip also supports specifiers containing `environment markers
<https://www.python.org/dev/peps/pep-0508/#environment-markers>`__ like so:
::
SomeProject ==5.4 ; python_version < '3.8'
SomeProject; sys_platform == 'win32'
Since version 19.1, pip also supports `direct references
<https://www.python.org/dev/peps/pep-0440/#direct-references>`__ like so:
::
SomeProject @ file:///somewhere/...
Environment markers are supported in the command line and in requirements files.
.. note::
Use quotes around specifiers in the shell when using ``>``, ``<``, or when
using environment markers. Don't use quotes in requirement files. [1]_
.. _`Per-requirement Overrides`:
This section has been moved to :doc:`../reference/requirement-specifiers`.
Per-requirement Overrides
-------------------------
Since version 7.0 pip supports controlling the command line options given to
``setup.py`` via requirements files. This disables the use of wheels (cached or
otherwise) for that package, as ``setup.py`` does not exist for wheels.
The ``--global-option`` and ``--install-option`` options are used to pass
options to ``setup.py``. For example:
::
FooProject >= 1.2 --global-option="--no-user-cfg" \
--install-option="--prefix='/usr/local'" \
--install-option="--no-compile"
The above translates roughly into running FooProject's ``setup.py``
script as:
::
python setup.py --no-user-cfg install --prefix='/usr/local' --no-compile
Note that the only way of giving more than one option to ``setup.py``
is through multiple ``--global-option`` and ``--install-option``
options, as shown in the example above. The value of each option is
passed as a single argument to the ``setup.py`` script. Therefore, a
line such as the following is invalid and would result in an
installation error.
::
# Invalid. Please use '--install-option' twice as shown above.
FooProject >= 1.2 --install-option="--prefix=/usr/local --no-compile"
This is now covered in :doc:`../reference/requirements-file-format`.
.. _`Pre Release Versions`:
@ -312,308 +239,25 @@ Wheel Cache
This is now covered in :doc:`../topics/caching`.
.. _`hash-checking mode`:
Hash-Checking Mode
Hash checking mode
------------------
Since version 8.0, pip can check downloaded package archives against local
hashes to protect against remote tampering. To verify a package against one or
more hashes, add them to the end of the line::
This is now covered in :doc:`../topics/secure-installs`.
FooProject == 1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 \
--hash=sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7
(The ability to use multiple hashes is important when a package has both
binary and source distributions or when it offers binary distributions for a
variety of platforms.)
The recommended hash algorithm at the moment is sha256, but stronger ones are
allowed, including all those supported by ``hashlib``. However, weaker ones
such as md5, sha1, and sha224 are excluded to avoid giving a false sense of
security.
Hash verification is an all-or-nothing proposition. Specifying a ``--hash``
against any requirement not only checks that hash but also activates a global
*hash-checking mode*, which imposes several other security restrictions:
* Hashes are required for all requirements. This is because a partially-hashed
requirements file is of little use and thus likely an error: a malicious
actor could slip bad code into the installation via one of the unhashed
requirements. Note that hashes embedded in URL-style requirements via the
``#md5=...`` syntax suffice to satisfy this rule (regardless of hash
strength, for legacy reasons), though you should use a stronger
hash like sha256 whenever possible.
* Hashes are required for all dependencies. An error results if there is a
dependency that is not spelled out and hashed in the requirements file.
* Requirements that take the form of project names (rather than URLs or local
filesystem paths) must be pinned to a specific version using ``==``. This
prevents a surprising hash mismatch upon the release of a new version
that matches the requirement specifier.
* ``--egg`` is disallowed, because it delegates installation of dependencies
to setuptools, giving up pip's ability to enforce any of the above.
.. _`--require-hashes`:
Hash-checking mode can be forced on with the ``--require-hashes`` command-line
option:
.. tab:: Unix/macOS
.. code-block:: console
$ python -m pip install --require-hashes -r requirements.txt
...
Hashes are required in --require-hashes mode (implicitly on when a hash is
specified for any package). These requirements were missing hashes,
leaving them open to tampering. These are the hashes the downloaded
archives actually had. You can add lines like these to your requirements
files to prevent tampering.
pyelasticsearch==1.0 --hash=sha256:44ddfb1225054d7d6b1d02e9338e7d4809be94edbe9929a2ec0807d38df993fa
more-itertools==2.2 --hash=sha256:93e62e05c7ad3da1a233def6731e8285156701e3419a5fe279017c429ec67ce0
.. tab:: Windows
.. code-block:: console
C:\> py -m pip install --require-hashes -r requirements.txt
...
Hashes are required in --require-hashes mode (implicitly on when a hash is
specified for any package). These requirements were missing hashes,
leaving them open to tampering. These are the hashes the downloaded
archives actually had. You can add lines like these to your requirements
files to prevent tampering.
pyelasticsearch==1.0 --hash=sha256:44ddfb1225054d7d6b1d02e9338e7d4809be94edbe9929a2ec0807d38df993fa
more-itertools==2.2 --hash=sha256:93e62e05c7ad3da1a233def6731e8285156701e3419a5fe279017c429ec67ce0
This can be useful in deploy scripts, to ensure that the author of the
requirements file provided hashes. It is also a convenient way to bootstrap
your list of hashes, since it shows the hashes of the packages it fetched. It
fetches only the preferred archive for each package, so you may still need to
add hashes for alternatives archives using :ref:`pip hash`: for instance if
there is both a binary and a source distribution.
The :ref:`wheel cache <Wheel cache>` is disabled in hash-checking mode to
prevent spurious hash mismatch errors. These would otherwise occur while
installing sdists that had already been automatically built into cached wheels:
those wheels would be selected for installation, but their hashes would not
match the sdist ones from the requirements file. A further complication is that
locally built wheels are nondeterministic: contemporary modification times make
their way into the archive, making hashes unpredictable across machines and
cache flushes. Compilation of C code adds further nondeterminism, as many
compilers include random-seeded values in their output. However, wheels fetched
from index servers are the same every time. They land in pip's HTTP cache, not
its wheel cache, and are used normally in hash-checking mode. The only downside
of having the wheel cache disabled is thus extra build time for sdists, and
this can be solved by making sure pre-built wheels are available from the index
server.
Hash-checking mode also works with :ref:`pip download` and :ref:`pip wheel`.
See :doc:`../topics/repeatable-installs` for a comparison of hash-checking mode
with other repeatability strategies.
.. warning::
Beware of the ``setup_requires`` keyword arg in :file:`setup.py`. The
(rare) packages that use it will cause those dependencies to be downloaded
by setuptools directly, skipping pip's hash-checking. If you need to use
such a package, see :ref:`Controlling
setup_requires<controlling-setup-requires>`.
.. warning::
Be careful not to nullify all your security work when you install your
actual project by using setuptools directly: for example, by calling
``python setup.py install``, ``python setup.py develop``, or
``easy_install``. Setuptools will happily go out and download, unchecked,
anything you missed in your requirements file—and its easy to miss things
as your project evolves. To be safe, install your project using pip and
:ref:`--no-deps <install_--no-deps>`.
Instead of ``python setup.py develop``, use...
.. tab:: Unix/macOS
.. code-block:: shell
python -m pip install --no-deps -e .
.. tab:: Windows
.. code-block:: shell
py -m pip install --no-deps -e .
Instead of ``python setup.py install``, use...
.. tab:: Unix/macOS
.. code-block:: shell
python -m pip install --no-deps .
.. tab:: Windows
.. code-block:: shell
py -m pip install --no-deps .
Hashes from PyPI
^^^^^^^^^^^^^^^^
PyPI provides an MD5 hash in the fragment portion of each package download URL,
like ``#md5=123...``, which pip checks as a protection against download
corruption. Other hash algorithms that have guaranteed support from ``hashlib``
are also supported here: sha1, sha224, sha384, sha256, and sha512. Since this
hash originates remotely, it is not a useful guard against tampering and thus
does not satisfy the ``--require-hashes`` demand that every package have a
local hash.
Local project installs
Local Project Installs
----------------------
pip supports installing local project in both regular mode and editable mode.
You can install local projects by specifying the project path to pip:
This is now covered in :doc:`../topics/local-project-installs`.
.. tab:: Unix/macOS
.. code-block:: shell
python -m pip install path/to/SomeProject
.. tab:: Windows
.. code-block:: shell
py -m pip install path/to/SomeProject
During regular installation, pip will copy the entire project directory to a
temporary location and install from there. The exception is that pip will
exclude .tox and .nox directories present in the top level of the project from
being copied. This approach is the cause of several performance and correctness
issues, so it is planned that pip 21.3 will change to install directly from the
local project directory. Depending on the build backend used by the project,
this may generate secondary build artifacts in the project directory, such as
the ``.egg-info`` and ``build`` directories in the case of the setuptools
backend.
To opt in to the future behavior, specify the ``--use-feature=in-tree-build``
option in pip's command line.
.. _`editable-installs`:
"Editable" Installs
^^^^^^^^^^^^^^^^^^^
"Editable" installs are fundamentally `"setuptools develop mode"
<https://setuptools.readthedocs.io/en/latest/userguide/development_mode.html>`_
installs.
You can install local projects or VCS projects in "editable" mode:
.. tab:: Unix/macOS
.. code-block:: shell
python -m pip install -e path/to/SomeProject
python -m pip install -e git+http://repo/my_project.git#egg=SomeProject
.. tab:: Windows
.. code-block:: shell
py -m pip install -e path/to/SomeProject
py -m pip install -e git+http://repo/my_project.git#egg=SomeProject
(See the :doc:`../topics/vcs-support` section above for more information on VCS-related syntax.)
For local projects, the "SomeProject.egg-info" directory is created relative to
the project path. This is one advantage over just using ``setup.py develop``,
which creates the "egg-info" directly relative the current working directory.
.. _`controlling-setup-requires`:
Controlling setup_requires
--------------------------
Setuptools offers the ``setup_requires`` `setup() keyword
<https://setuptools.readthedocs.io/en/latest/userguide/keywords.html>`_
for specifying dependencies that need to be present in order for the
``setup.py`` script to run. Internally, Setuptools uses ``easy_install``
to fulfill these dependencies.
pip has no way to control how these dependencies are located. None of the
package index options have an effect.
The solution is to configure a "system" or "personal" `Distutils configuration
file
<https://docs.python.org/3/install/index.html#distutils-configuration-files>`_ to
manage the fulfillment.
For example, to have the dependency located at an alternate index, add this:
::
[easy_install]
index_url = https://my.index-mirror.com
To have the dependency located from a local directory and not crawl PyPI, add this:
::
[easy_install]
allow_hosts = ''
find_links = file:///path/to/local/archives/
Editable installs
-----------------
This is now covered in :doc:`../topics/local-project-installs`.
Build System Interface
----------------------
In order for pip to install a package from source, ``setup.py`` must implement
the following commands::
setup.py egg_info [--egg-base XXX]
setup.py install --record XXX [--single-version-externally-managed] [--root XXX] [--compile|--no-compile] [--install-headers XXX]
The ``egg_info`` command should create egg metadata for the package, as
described in the setuptools documentation at
https://setuptools.readthedocs.io/en/latest/userguide/commands.html#egg-info-create-egg-metadata-and-set-build-tags
The ``install`` command should implement the complete process of installing the
package to the target directory XXX.
To install a package in "editable" mode (``pip install -e``), ``setup.py`` must
implement the following command::
setup.py develop --no-deps
This should implement the complete process of installing the package in
"editable" mode.
All packages will be attempted to built into wheels::
setup.py bdist_wheel -d XXX
One further ``setup.py`` command is invoked by ``pip install``::
setup.py clean
This command is invoked to clean up temporary commands from the build. (TODO:
Investigate in more detail when this command is required).
No other build system commands are invoked by the ``pip install`` command.
Installing a package from a wheel does not invoke the build system at all.
.. _PyPI: https://pypi.org/
.. _setuptools extras: https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#optional-dependencies
This is now covered in :doc:`../reference/build-system/index`.
.. _`pip install Options`:
@ -743,7 +387,7 @@ Examples
py -m pip install -e git+https://git.repo/some_pkg.git@feature#egg=SomePackage # from 'feature' branch
py -m pip install -e "git+https://git.repo/some_repo.git#egg=subdir&subdirectory=subdir_path" # install a python package from a repo subdirectory
#. Install a package with `setuptools extras`_.
#. Install a package with `extras`_.
.. tab:: Unix/macOS
@ -902,7 +546,5 @@ Examples
py -m pip install SomePackage1 SomePackage2 --no-binary SomePackage1
----
.. [1] This is true with the exception that pip v7.0 and v7.0.1 required quotes
around specifiers containing environment markers in requirement files.
.. _extras: https://www.python.org/dev/peps/pep-0508/#extras
.. _PyPI: https://pypi.org/

View file

@ -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`` command, ``pip list --format=freeze`` will not
report editable install information, but the version of the package at the
time it was installed.

View file

@ -28,60 +28,7 @@ Description
Build System Interface
----------------------
In order for pip to build a wheel, ``setup.py`` must implement the
``bdist_wheel`` command with the following syntax:
.. tab:: Unix/macOS
.. code-block:: shell
python setup.py bdist_wheel -d TARGET
.. tab:: Windows
.. code-block:: shell
py setup.py bdist_wheel -d TARGET
This command must create a wheel compatible with the invoking Python
interpreter, and save that wheel in the directory TARGET.
No other build system commands are invoked by the ``pip wheel`` command.
Customising the build
^^^^^^^^^^^^^^^^^^^^^
It is possible using ``--global-option`` to include additional build commands
with their arguments in the ``setup.py`` command. This is currently the only
way to influence the building of C extensions from the command line. For
example:
.. tab:: Unix/macOS
.. code-block:: shell
python -m pip wheel --global-option bdist_ext --global-option -DFOO wheel
.. tab:: Windows
.. code-block:: shell
py -m pip wheel --global-option bdist_ext --global-option -DFOO wheel
will result in a build command of
::
setup.py bdist_ext -DFOO bdist_wheel -d TARGET
which passes a preprocessor symbol to the extension build.
Such usage is considered highly build-system specific and more an accident of
the current implementation than a supported interface.
This is now covered in :doc:`../reference/build-system/index`.
Options
=======

View file

@ -55,6 +55,7 @@ print("pip release:", release)
# -- Options for myst-parser ----------------------------------------------------------
myst_enable_extensions = ["deflist"]
myst_heading_anchors = 3
# -- Options for smartquotes ----------------------------------------------------------

View file

@ -46,7 +46,7 @@ The ``README``, license, ``pyproject.toml``, ``setup.py``, and so on are in the
* ``__init__.py``
* ``conftest.py``
* ``data/`` *[test data for running tests -- pesudo package index in it! Lots of small packages that are invalid or are valid. Test fixtures. Used by functional tests]*
* ``data/`` *[test data for running tests -- pseudo package index in it! Lots of small packages that are invalid or are valid. Test fixtures. Used by functional tests]*
* ``functional/`` *[functional tests of pips CLI -- end-to-end, invoke pip in subprocess & check results of execution against desired result. This also is what makes test suite slow]*
* ``lib/`` *[helpers for tests]*
* ``unit/`` *[unit tests -- fast and small and nice!]*

View file

@ -18,17 +18,17 @@ Supported interpreters
pip support a variety of Python interpreters:
- CPython 3.6
- CPython 3.7
- CPython 3.8
- CPython 3.9
- CPython 3.10
- Latest PyPy3
on different operating systems:
- Linux
- Windows
- MacOS
- macOS
and on different architectures:
@ -77,9 +77,9 @@ Developer tasks
======== =============== ================ ================== =============
OS docs lint vendoring packaging
======== =============== ================ ================== =============
Linux Github Github Github Github
Windows Github Github Github Github
MacOS Github Github Github Github
Linux GitHub GitHub GitHub GitHub
Windows GitHub GitHub GitHub GitHub
macOS GitHub GitHub GitHub GitHub
======== =============== ================ ================== =============
Actual testing
@ -88,28 +88,26 @@ Actual testing
+------------------------------+---------------+-----------------+
| **interpreter** | **unit** | **integration** |
+-----------+----------+-------+---------------+-----------------+
| | | CP3.6 | | |
| | +-------+---------------+-----------------+
| | x86 | CP3.7 | | |
| | +-------+---------------+-----------------+
| | | CP3.8 | | |
| | +-------+---------------+-----------------+
| | | CP3.9 | | |
| | +-------+---------------+-----------------+
| | | CP3.10| | |
| | +-------+---------------+-----------------+
| | | PyPy3 | | |
| Windows +----------+-------+---------------+-----------------+
| | | CP3.6 | Github | Github |
| | +-------+---------------+-----------------+
| | x64 | CP3.7 | | |
| | x64 | CP3.7 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | CP3.8 | | |
| | +-------+---------------+-----------------+
| | | CP3.9 | Github | Github |
| | | CP3.9 | | |
| | +-------+---------------+-----------------+
| | | CP3.10| GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | PyPy3 | | |
+-----------+----------+-------+---------------+-----------------+
| | | CP3.6 | | |
| | +-------+---------------+-----------------+
| | x86 | CP3.7 | | |
| | +-------+---------------+-----------------+
| | | CP3.8 | | |
@ -118,33 +116,33 @@ Actual testing
| | +-------+---------------+-----------------+
| | | PyPy3 | | |
| Linux +----------+-------+---------------+-----------------+
| | | CP3.6 | Github | Github |
| | x64 | CP3.7 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | x64 | CP3.7 | Github | Github |
| | | CP3.8 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | CP3.8 | Github | Github |
| | | CP3.9 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | CP3.9 | Github | Github |
| | | CP3.10| GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | PyPy3 | | |
+-----------+----------+-------+---------------+-----------------+
| | | CP3.6 | | |
| | +-------+---------------+-----------------+
| | x86 | CP3.7 | | |
| | arm64 | CP3.7 | | |
| | +-------+---------------+-----------------+
| | | CP3.8 | | |
| | +-------+---------------+-----------------+
| | | CP3.9 | | |
| | +-------+---------------+-----------------+
| | | CP3.10| | |
| | +-------+---------------+-----------------+
| | | PyPy3 | | |
| MacOS +----------+-------+---------------+-----------------+
| | | CP3.6 | Github | Github |
| macOS +----------+-------+---------------+-----------------+
| | x64 | CP3.7 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | x64 | CP3.7 | Github | Github |
| | | CP3.8 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | CP3.8 | Github | Github |
| | | CP3.9 | GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | CP3.9 | Github | Github |
| | | CP3.10| GitHub | GitHub |
| | +-------+---------------+-----------------+
| | | PyPy3 | | |
+-----------+----------+-------+---------------+-----------------+

View file

@ -27,8 +27,8 @@ Development Environment
pip is a command line application written in Python. For developing pip,
you should `install Python`_ on your computer.
For developing pip, you need to install :pypi:`tox`. Often, you can run
``python -m pip install tox`` to install and use it.
For developing pip, you need to install :pypi:`nox`. Often, you can run
``python -m pip install nox`` to install and use it.
Running pip From Source Tree
@ -42,8 +42,8 @@ You can then invoke your local source tree pip normally.
.. code-block:: shell
virtualenv venv # You can also use "python -m venv venv" from python3.3+
source venv/bin/activate
python -m venv .venv
source .venv/bin/activate
python -m pip install -e .
python -m pip --version
@ -51,8 +51,8 @@ You can then invoke your local source tree pip normally.
.. code-block:: shell
virtualenv venv # You can also use "py -m venv venv" from python3.3+
venv\Scripts\activate
py -m venv .venv
.venv\Scripts\activate
py -m pip install -e .
py -m pip --version
@ -60,7 +60,7 @@ Running Tests
=============
pip's tests are written using the :pypi:`pytest` test framework and
:mod:`unittest.mock`. :pypi:`tox` is used to automate the setup and execution
:mod:`unittest.mock`. :pypi:`nox` is used to automate the setup and execution
of pip's tests.
It is preferable to run the tests in parallel for better experience during development,
@ -70,38 +70,39 @@ To run tests:
.. code-block:: console
$ tox -e py36 -- -n auto
$ nox -s test-3.10 -- -n auto
To run tests without parallelization, run:
.. code-block:: console
$ tox -e py36
$ nox -s test-3.10
The example above runs tests against Python 3.6. You can also use other
versions like ``py39`` and ``pypy3``.
The example above runs tests against Python 3.10. You can also use other
versions like ``3.9`` and ``pypy3``.
``tox`` has been configured to forward any additional arguments it is given to
``nox`` has been configured to forward any additional arguments it is given to
``pytest``. This enables the use of pytest's `rich CLI`_. As an example, you
can select tests using the various ways that pytest provides:
.. code-block:: console
$ # Using file name
$ tox -e py36 -- tests/functional/test_install.py
$ nox -s test-3.10 -- tests/functional/test_install.py
$ # Using markers
$ tox -e py36 -- -m unit
$ nox -s test-3.10 -- -m unit
$ # Using keywords
$ tox -e py36 -- -k "install and not wheel"
$ nox -s test-3.10 -- -k "install and not wheel"
Running pip's test suite requires supported version control tools (subversion,
bazaar, git, and mercurial) to be installed. If you are missing one of the VCS
tools, you can tell pip to skip those tests:
Running pip's entire test suite requires supported version control tools
(subversion, bazaar, git, and mercurial) to be installed. If you are missing
any of these VCS, those tests should be skipped automatically. You can also
explicitly tell pytest to skip those tests:
.. code-block:: console
$ tox -e py36 -- -k "not svn"
$ tox -e py36 -- -k "not (svn or git)"
$ nox -s test-3.10 -- -k "not svn"
$ nox -s test-3.10 -- -k "not (svn or git)"
Running Linters
@ -115,7 +116,7 @@ To use linters locally, run:
.. code-block:: console
$ tox -e lint
$ nox -s lint
.. note::
@ -125,6 +126,25 @@ To use linters locally, run:
readability problems.
Running pip under a debugger
============================
In order to debug pip's behavior, you can run it under a debugger like so:
.. code-block:: console
$ python -m pdb -m pip --debug ...
Replace the ``...`` with arguments you'd like to run pip with. Give PDB the
``c`` ("continue") command afterwards, to run the process.
The ``--debug`` flag disables pip's exception handler, which would normally
catch all unhandled exceptions. With this flag, pip will let these exceptions
propagate outside of its main subroutine, letting them get caught by the
debugger. This way you'll be able to debug an exception post-mortem via PDB.
Building Documentation
======================
@ -135,7 +155,7 @@ To build it locally, run:
.. code-block:: console
$ tox -e docs
$ nox -s docs
The built documentation can be found in the ``docs/build`` folder.

View file

@ -137,7 +137,7 @@ The lifecycle of an issue (bug or support) generally looks like:
#. waiting for triage (marked with label ``triage``)
#. confirming issue - some discussion with the user, gathering
details, trying to reproduce the issue (may be marked with a specific
category, ``S: awaiting-respose``, ``S: discussion-needed``, or
category, ``S: awaiting-response``, ``S: discussion-needed``, or
``S: need-repro``)
#. confirmed - the issue is pretty consistently reproducible in a
straightforward way, or a mechanism that could be causing the issue has been

View file

@ -14,7 +14,7 @@ If your Python environment does not have pip installed, there are 2 mechanisms
to install pip supported directly by pip's maintainers:
- [`ensurepip`](#ensurepip)
- [`get-pip.py`](#get-pip-py)
- [`get-pip.py`](#get-pippy)
### `ensurepip`
@ -75,14 +75,15 @@ $ pip install --upgrade pip
The current version of pip works on:
- Windows, Linux and MacOS.
- CPython 3.6, 3.7, 3.8, 3.9, 3.10 and latest PyPy3.
- CPython 3.7, 3.8, 3.9, 3.10 and latest PyPy3.
pip is tested to work on the latest patch version of the Python interpreter,
for each of the minor versions listed above. Previous patch versions are
supported on a best effort approach.
pip's maintainers do not provide support for users on older versions of Python,
and these users should request for support from the relevant provider
(eg: Linux distro community, cloud provider support channels, etc).
Other operating systems and Python versions are not supported by pip's
maintainers.
Users who are on unsupported platforms should be aware that if they hit issues, they may have to resolve them for themselves. If they received pip from a source which provides support for their platform, they should request pip support from that source.
[^python]: The `ensurepip` module was added to the Python standard library in Python 3.4.

View file

@ -7,6 +7,6 @@ Changelog
Major and minor releases of pip also include changes listed within
prior beta releases.
.. towncrier-draft-entries:: |release|, unreleased as on
.. towncrier-draft-entries:: Not yet released
.. pip-news-include:: ../../NEWS.rst

View file

@ -0,0 +1,127 @@
(build-interface)=
# Build System Interface
When dealing with installable source distributions of a package, pip does not
directly handle the build process for the package. This responsibility is
delegated to "build backends" -- also known as "build systems". This means
that pip needs an interface, to interact with these build backends.
There are two main interfaces that pip uses for these interactions:
```{toctree}
:hidden:
pyproject-toml
setup-py
```
<!-- prettier-ignore-start -->
[`pyproject.toml` based](pyproject-toml)
: Standards-backed interface, that has explicit declaration and management of
build dependencies.
[`setup.py` based](setup-py)
: Legacy interface, that we're working to migrate users away from. Has no good
mechanisms to declare build dependencies.
<!-- prettier-ignore-end -->
Details on the individual interfaces can be found on their dedicated pages,
linked above. This document covers the nuances around which build system
interface pip will use for a project, as well as details that apply to all
the build system interfaces that pip may use.
## Determining which build system interface is used
Currently, pip uses the `pyproject.toml` based build system interface, if a
`pyproject.toml` file exists. If not, the legacy build system interface is used.
The intention is to switch to using the `pyproject.toml` build system interface
unconditionally and to drop support for the legacy build system interface at
some point in the future.
When performing a build, pip will mention which build system interface it is
using. Typically, this will take the form of a message like:
```none
Building wheel for pip (pyproject.toml)... done
```
```none
Building wheel for pip (setup.py)... done
```
The content in the brackets, refers to which build system interface is being
used.
```{versionchanged} 21.3
The output uses "pyproject.toml" instead of "PEP 517" to refer to be
`pyproject.toml` based build system interface.
```
## Controlling which build system interface is used
The [`--use-pep517`](install_--use-pep517) flag (and corresponding environment
variable: `PIP_USE_PEP517`) can be used to force all packages to build using
the `pyproject.toml` based build system interface. There is no way to force
the use of the legacy build system interface.
(controlling-setup_requires)=
## Controlling `setup_requires`
```{hint}
This is only relevant for projects that use setuptools as the build backend,
and use the `setup_requires` keyword argument in their setup.py file.
```
The `setup_requires` argument in `setup.py` is used to specify build-time
dependencies for a package. This has been superseded by the
`build-system.requires` key in `pyproject.toml` files (per {pep}`518`).
However, there are situations where you might encounter a package that uses
`setup_requires` (eg: the package has not been updated to use the newer
approach yet!).
If you control the package, consider adding a `pyproject.toml` file to utilise
the modern build system interface. That avoids invoking the problematic
behaviour by deferring to pip for the installations.
For the end users, the best solution for dealing with packages with
`setup_requires` is to install the packages listed in `setup_requires`
beforehand, using a prior `pip install` command. This is because there is no
way to control how these dependencies are located by `easy_install`, or how
setuptools will invoke `pip` using pip's command line options -- which makes it
tricky to get things working appropriately.
If you wish to ensure that `easy_install` invocations do not reach out to PyPI,
you will need to configure its behaviour using a
[`distutils` configuration file][distutils-config]. Here are some examples:
- To have the dependency located at an alternate index with `easy_install`
```ini
[easy_install]
index_url = https://my.index-mirror.com
```
- To have the dependency located from a local directory and not crawl PyPI, add this:
```ini
[easy_install]
allow_hosts = ''
find_links = file:///path/to/local/archives/
```
```{admonition} Historical context
`setuptools < 52.0` will use `easy_install` to try to fulfill `setup_requires`
dependencies, which can result in weird failures -- `easy_install` does not
understand many of the modern Python packaging standards, and will usually
attempt to install incompatible package versions or to build packages
incorrectly. It also generates improper script wrappers, which don't do the
right thing in many situations.
Newer versions of `setuptools` will use `pip` for these installations, but have
limited ability to pass through any command line arguments. This can also result
in weird failures and subtly-incorrect behaviour.
```
[distutils-config]: https://docs.python.org/3/install/index.html#distutils-configuration-files

View file

@ -0,0 +1,146 @@
# `pyproject.toml`
```{versionadded} 10.0
```
Modern Python packages can contain a `pyproject.toml` file, first introduced in
{pep}`518` and later expanded in {pep}`517`, {pep}`621` and {pep}`660`.
This file contains build system requirements and information, which are used by
pip to build the package.
## Build process
The overall process for building a package is:
- Create an isolated build environment.
- Populate the build environment with build dependencies.
- Generate the package's metadata, if necessary and possible.
- Generate a wheel for the package.
The wheel can then be used to perform an installation, if necessary.
### Build Isolation
For building packages using this interface, pip uses an _isolated environment_.
That is, pip will install build-time Python dependencies in a temporary
directory which will be added to `sys.path` for the build commands. This ensures
that build requirements are handled independently of the user's runtime
environment.
For example, a project that needs an older version of setuptools to build can
still be installed, even if the user has an newer version installed (and
without silently replacing that version).
### Build-time dependencies
Introduced in {pep}`518`, the `build-system.requires` key in the
`pyproject.toml` file is a list of requirement specifiers for build-time
dependencies of a package.
```toml
[build-system]
requires = ["setuptools ~= 58.0", "cython ~= 0.29.0"]
```
It is also possible for a build backend to provide dynamically calculated
build dependencies, using {pep}`517`'s `get_requires_for_build_wheel` hook. This
hook will be called by pip, and dependencies it describes will also be installed
in the build environment. For example, newer versions of setuptools expose the
contents of `setup_requires` to pip via this hook.
### Metadata Generation
```{versionadded} 19.0
```
Once the build environment has been created and populated with build-time
dependencies, `pip` will usually need metadata about a package (name, version,
dependencies, and more).
If {pep}`517`'s `prepare_metadata_for_build_wheel` hook is provided by the
build backend, that will be used to generate the packages' metadata. Otherwise,
a wheel will be generated (as described below) and the metadata contained
within such a wheel will be used.
### Wheel Generation
```{versionadded} 19.0
```
For generating a wheel, pip uses the {pep}`517` `build_wheel` hook that has
to be provided by the build backend. The build backend will generate a wheel,
which may involve compiling extension code written in C/C++ (or other
languages).
Wheels generated using this mechanism can be [cached](wheel-caching) for reuse,
to speed up future installations.
### Editable Installation
```{versionadded} 21.3
```
For performing editable installs, pip will use {pep}`660`
`build_wheel_for_editable` hook that has to be provided by the build backend.
The wheels generated using this mechanism are not cached.
```{admonition} Compatibility fallback
If this hook is missing on the build backend _and_ there's a `setup.py` file
in the project, pip will fallback to the legacy setup.py-based editable
installation.
This is considered a stopgap solution until setuptools adds support for
{pep}`660`, at which point this functionality will be removed; following pip's
regular {ref}`deprecation policy <Deprecation Policy>`.
```
## Build output
It is the responsibility of the build backend to ensure that the output is
in the correct encoding, as described in {pep}`517`. This likely involves
dealing with [the same challenges as pip has for legacy builds](build-output).
## Fallback Behaviour
If a project does not have a `pyproject.toml` file containing a `build-system`
section, it will be assumed to have the following backend settings:
```toml
[build-system]
requires = ["setuptools>=40.8.0", "wheel"]
build-backend = "setuptools.build_meta:__legacy__"
```
If a project has a `build-system` section but no `build-backend`, then:
- It is expected to include `setuptools` and `wheel` as build requirements. An
error is reported if the available version of `setuptools` is not recent
enough.
- The `setuptools.build_meta:__legacy__` build backend will be used.
## Disabling build isolation
This can be disabled using the `--no-build-isolation` flag -- users supplying
this flag are responsible for ensuring the build environment is managed
appropriately, including ensuring that all required build-time dependencies are
installed, since pip does not manage build-time dependencies when this flag is
passed.
## Historical notes
As this feature was incrementally rolled out, there have been various notable
changes and improvements in it.
- setuptools 40.8.0 is the first version of setuptools that offers a
{pep}`517` backend that closely mimics directly executing `setup.py`.
- Prior to pip 18.0, pip only supports installing build requirements from
wheels, and does not support the use of environment markers and extras (only
version specifiers are respected).
- Prior to pip 18.1, build dependencies using `.pth` files are not properly
supported; as a result namespace packages do not work under Python 3.2 and
earlier.

View file

@ -0,0 +1,133 @@
# `setup.py` (legacy)
Prior to the introduction of pyproject.toml-based builds (in {pep}`517` and
{pep}`518`), pip had only supported installing packages using `setup.py` files
that were built using {pypi}`setuptools`.
The interface documented here is retained currently solely for legacy purposes,
until the migration to `pyproject.toml`-based builds can be completed.
```{caution}
The arguments and syntax of the various invocations of `setup.py` made by
pip, are considered an implementation detail that is strongly coupled with
{pypi}`setuptools`. This build system interface is not meant to be used by any
other build backend, which should be based on the {doc}`pyproject-toml` build
system interface instead.
Further, projects should _not_ expect to rely on there being any form of
backward compatibility guarantees around the `setup.py` interface.
```
## Build process
The overall process for building a package is:
- Generate the package's metadata.
- Generate a wheel for the package.
- If this fails and we're trying to install the package, attempt a direct
installation.
The wheel can then be used to perform an installation, if necessary.
### Metadata Generation
As a first step, `pip` needs to get metadata about a package (name, version,
dependencies, and more). It collects this by calling `setup.py egg_info`.
The `egg_info` command generates the metadata for the package, which pip can
then consume and proceed to gather all the dependencies of the package. Once
the dependency resolution process is complete, pip will proceed to the next
stage of the build process for these packages.
### Wheel Generation
When provided with a {term}`pypug:source distribution (or "sdist")` for a
package, pip will attempt to build a {term}`pypug:wheel`. Since wheel
distributions can be [cached](wheel-caching), this can greatly speed up future
installations for the package.
This is done by calling `setup.py bdist_wheel` which requires the {pypi}`wheel`
package to be installed.
If this wheel generation is successful (this can include compiling C/C++ code,
depending on the package), the generated wheel is added to pip's wheel cache
and will be used for this installation. The built wheel is cached locally
by pip to avoid repeated identical builds.
If this wheel generation fails, pip runs `setup.py clean` to clean up any build
artifacts that may have been generated. After that, pip will attempt a direct
installation.
### Direct Installation
When all else fails, pip will invoke `setup.py install` to install a package
using setuptools' mechanisms to perform the installation. This is currently the
last-resort fallback for projects that cannot be built into wheels, and may not
be supported in the future.
### Editable Installation
For installing packages in "editable" mode
({ref}`pip install --editable <install_--editable>`), pip will invoke
`setup.py develop`, which will use setuptools' mechanisms to perform an
editable/development installation.
## Setuptools Injection
To support projects that directly use `distutils`, pip injects `setuptools` into
`sys.modules` before invoking `setup.py`. This injection should be transparent
to `distutils`-based projects.
## Customising the build
The `--global-option` and `--build-option` arguments to the `pip install`
and `pip wheel` inject additional arguments into the `setup.py` command
(`--build-option` is only available in `pip wheel`).
```{attention}
The use of `--global-option` and `--build-option` is highly setuptools
specific, and is considered more an accident of the current implementation than
a supported interface. It is documented here for completeness. These flags will
not be supported, once this build system interface is dropped.
```
These arguments are included in the command as follows:
```
python setup.py <global_options> BUILD COMMAND <build_options>
```
The options are passed unmodified, and presently offer direct access to the
distutils command line. For example:
```{pip-cli}
$ pip wheel --global-option bdist_ext --global-option -DFOO wheel
```
will result in pip invoking:
```
setup.py bdist_ext -DFOO bdist_wheel -d TARGET
```
This passes a preprocessor symbol to the extension build.
(build-output)=
## Build Output
Any output produced by the build system will be read by pip (for display to the
user if requested). In order to correctly read the build system output, pip
requires that the output is written in a well-defined encoding, specifically
the encoding the user has configured for text output (which can be obtained in
Python using `locale.getpreferredencoding`). If the configured encoding is
ASCII, pip assumes UTF-8 (to account for the behaviour of some Unix systems).
Build systems should ensure that any tools they invoke (compilers, etc) produce
output in the correct encoding. In practice - and in particular on Windows,
where tools are inconsistent in their use of the "OEM" and "ANSI" codepages -
this may not always be possible. pip will therefore attempt to recover cleanly
if presented with incorrectly encoded build tool output, by translating
unexpected byte sequences to Python-style hexadecimal escape sequences
(`"\x80\xff"`, etc). However, it is still possible for output to be displayed
using an incorrect encoding (mojibake).

View file

@ -6,5 +6,7 @@ interoperability standards that pip utilises/implements.
```{toctree}
:titlesonly:
build-system/index
requirement-specifiers
requirements-file-format
```

View file

@ -0,0 +1,61 @@
(Requirement Specifiers)=
# Requirement Specifiers
pip supports installing from a package index using a {term}`requirement specifier <pypug:Requirement Specifier>`. Generally speaking, a requirement specifier is composed of a project name followed by optional {term}`version specifiers <pypug:Version Specifier>`.
{pep}`508` contains a full specification of the format of a requirement.
```{versionadded} 6.0
Support for environment markers.
```
```{versionadded} 19.1
Support for the direct URL reference form.
```
## Overview
A requirement specifier comes in two forms:
- name-based, which is composed of:
- a package name (eg: `requests`)
- optionally, a set of "extras" that serve to install optional dependencies (eg: `security`)
- optionally, constraints to apply on the version of the package
- optionally, environment markers
- URL-based, which is composed of:
- a package name (eg: `requests`)
- optionally, a set of "extras" that serve to install optional dependencies (eg: `security`)
- a URL for the package
- optionally, environment markers
## Examples
A few example name-based requirement specifiers:
```
SomeProject
SomeProject == 1.3
SomeProject >= 1.2, < 2.0
SomeProject[foo, bar]
SomeProject ~= 1.4.2
SomeProject == 5.4 ; python_version < '3.8'
SomeProject ; sys_platform == 'win32'
requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"
```
```{note}
Use quotes around specifiers in the shell when using `>`, `<`, or when using environment markers.
Do _not_ use quotes in requirement files. There is only one exception: pip v7.0 and v7.0.1 (from May 2015) required quotes around specifiers containing environment markers in requirement files.
```
A few example URL-based requirement specifiers:
```none
pip @ https://github.com/pypa/pip/archive/22.0.2.zip
requests [security] @ https://github.com/psf/requests/archive/refs/heads/main.zip ; python_version >= "3.11"
```

View file

@ -1,3 +1,5 @@
(requirements-file-format)=
# Requirements File Format
Requirements files serve as a list of items to be installed by pip, when
@ -13,13 +15,38 @@ consumption by pip, and other tools should take that into account before using
it for their own purposes.
```
## Example
```
# This is a comment, to show how #-prefixed lines are ignored.
# It is possible to specify requirements as plain names.
pytest
pytest-cov
beautifulsoup4
# The syntax supported here is the same as that of requirement specifiers.
docopt == 0.6.1
requests [security] >= 2.8.1, == 2.8.* ; python_version < "2.7"
urllib3 @ https://github.com/urllib3/urllib3/archive/refs/tags/1.26.8.zip
# It is possible to refer to other requirement files or constraints files.
-r other-requirements.txt
-c constraints.txt
# It is possible to refer to specific local distribution paths.
./downloads/numpy-1.9.2-cp34-none-win32.whl
# It is possible to refer to URLs.
http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl
```
## 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]...]`
- `<requirement specifier>`
- `<archive url/path>`
- `[-e] <local project path>`
- `[-e] <vcs project url>`
@ -72,13 +99,21 @@ and two {ref}`--find-links <install_--find-links>` locations:
```
````
(per-requirement-options)=
### Per-requirement options
```{versionadded} 7.0
```
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`)
- `--hash` (for {ref}`Hash-checking mode`)
## Referring to other requirements files
If you wish, you can refer to other requirements files, like this:
@ -116,30 +151,34 @@ 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
## Influencing the build system
```{danger}
This disables the use of wheels (cached or otherwise). This could mean that builds will be slower, less deterministic, less reliable and may not behave correctly upon installation.
This mechanism is only preserved for backwards compatibility and should be considered deprecated. A future release of pip may drop these options.
```
###### 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.*
The `--global-option` and `--install-option` options are used to pass options to `setup.py`.
###### Refer to other requirements files ######
-r other-requirements.txt
```{attention}
These options are highly coupled with how pip invokes setuptools using the {doc}`../reference/build-system/setup-py` build system interface. It is not compatible with newer {doc}`../reference/build-system/pyproject-toml` build system interface.
###### 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 is will not work with other build-backends or newer setup.cfg-only projects.
```
If you have a declaration like:
FooProject >= 1.2 --global-option="--no-user-cfg" \
--install-option="--prefix='/usr/local'" \
--install-option="--no-compile"
The above translates roughly into running FooProject's `setup.py` script as:
python setup.py --no-user-cfg install --prefix='/usr/local' --no-compile
Note that the only way of giving more than one option to `setup.py` is through multiple `--global-option` and `--install-option` options, as shown in the example above. The value of each option is passed as a single argument to the `setup.py` script. Therefore, a line such as the following is invalid and would result in an installation error.
# Invalid. Please use '--install-option' twice as shown above.
FooProject >= 1.2 --install-option="--prefix=/usr/local --no-compile"

View file

@ -1,6 +1,7 @@
# Caching
```{versionadded} 6.0
```
pip provides an on-by-default caching, designed to reduce the amount of time
@ -26,6 +27,8 @@ While this cache attempts to minimize network activity, it does not prevent
network access altogether. If you want a local install solution that
circumvents accessing PyPI, see {ref}`Installing from local packages`.
(wheel-caching)=
### Locally built wheels
pip attempts to use wheels from its local wheel cache whenever possible.
@ -38,6 +41,10 @@ wheel using the package's build system. If the build is successful, this wheel
is added to the cache and used in subsequent installs for the same package
version.
Wheels built from source distributions provided to pip as a direct path (such
as `pip install .`) are not cached across runs, though they may be reused within
the same `pip` execution.
```{versionchanged} 20.0
pip now caches wheels when building from an immutable Git reference
(i.e. a commit hash).
@ -79,7 +86,7 @@ implementation detail and may change between any two versions of pip.
## Disabling caching
pip's caching behaviour is disabled by passing the ``--no-cache-dir`` option.
pip's caching behaviour is disabled by passing the `--no-cache-dir` option.
It is, however, recommended to **NOT** disable pip's caching. Doing so can
significantly slow down pip (due to repeated operations and package builds)

View file

@ -29,11 +29,10 @@ complexity for backwards compatibility reasons.
```{tab} Unix
Global
: {file}`/etc/pip.conf`
: In a "pip" subdirectory of any of the paths set in the environment variable
`XDG_CONFIG_DIRS` (if it exists), for example {file}`/etc/xdg/pip/pip.conf`.
Alternatively, it may be in a "pip" subdirectory of any of the paths set
in the environment variable `XDG_CONFIG_DIRS` (if it exists), for
example {file}`/etc/xdg/pip/pip.conf`.
This will be followed by loading {file}`/etc/pip.conf`.
User
: {file}`$HOME/.config/pip/pip.conf`, which respects the `XDG_CONFIG_HOME` environment variable.

View file

@ -156,7 +156,7 @@ how to inspect:
During deployment, you can create a lockfile stating the exact package and
version number for for each dependency of that package. You can create this
with `pip-tools <https://github.com/jazzband/pip-tools/>`\_\_.
with [pip-tools](https://github.com/jazzband/pip-tools/).
This means the "work" is done once during development process, and thus
will avoid performing dependency resolution during deployment.
@ -277,10 +277,10 @@ your _dependency_ by:
- Requesting that the package maintainers loosen _their_ dependencies
- Forking the package and loosening the dependencies yourself
:::{warning}
```{warning}
If you choose to fork the package yourself, you are _opting out_ of
any support provided by the package maintainers. Proceed at your own risk!
:::
```
#### All requirements are appropriate, but a solution does not exist

View file

@ -14,6 +14,8 @@ authentication
caching
configuration
dependency-resolution
local-project-installs
repeatable-installs
secure-installs
vcs-support
```

View file

@ -0,0 +1,65 @@
# Local project installs
It is extremely common to have a project, available in a folder/directory on your computer [^1] that you wish to install.
With pip, depending on your usecase, there are two ways to do this:
- A regular install
- An editable install
## Regular installs
You can install local projects by specifying the project path to pip:
```{pip-cli}
$ pip install path/to/SomeProject
```
This will install the project into the Python that pip is associated with, in a manner similar to how it would actually be installed.
This is what should be used in CI system and for deployments, since it most closely mirrors how a package would get installed if you build a distribution and installed from it (because that's _exactly_ what it does).
(editable-installs)=
## Editable installs
You can install local projects in "editable" mode:
```{pip-cli}
$ pip install -e path/to/SomeProject
```
Editable installs allow you to install your project without copying any files. Instead, the files in the development directory are added to Python's import path. This approach is well suited for development and is also known as a "development installation".
With an editable install, you only need to perform a re-installation if you change the project metadata (eg: version, what scripts need to be generated etc). You will still need to run build commands when you need to perform a compilation for non-Python code in the project (eg: C extensions).
```{caution}
It is possible to see behaviour differences between regular installs vs editable installs. In case you distribute the project as a "distribution package", users will see the behaviour of regular installs -- thus, it is important to ensure that regular installs work correctly.
```
```{note}
This is functionally the same as [setuptools' develop mode], and that's precisely the mechanism used for setuptools-based projects.
There are two advantages over using `setup.py develop` directly:
- This works with non-setuptools build-backends as well.
- The ".egg-info" directory is created relative to the project path, when using pip. This is generally a better location than setuptools, which dumps it in the current working directory.
```
[setuptools' develop mode]: https://setuptools.readthedocs.io/en/latest/userguide/development_mode.html
## Build artifacts
```{versionchanged} 21.3
The project being installed is no longer copied to a temporary directory before invoking the build system.
```
This behaviour change has several consequences:
- Local project builds will now be significantly faster, for certain kinds of projects and on systems with slow I/O (eg: via network attached storage or overly aggressive antivirus software).
- Certain build backends (eg: `setuptools`) will litter the project directory with secondary build artifacts (eg: `.egg-info` directories).
- Certain build backends (eg: `setuptools`) may not be able to perform with parallel builds anymore, since they previously relied on the fact that pip invoked them in a separate directory for each build.
A `--use-deprecated=out-of-tree-build` option is available, until pip 22.1, as a mechanism to aid users with transitioning to the newer model of in-tree-builds.
[^1]: Specifically, the current machine's filesystem.

View file

@ -94,5 +94,5 @@ identical packages.
Beware of the `setup_requires` keyword arg in {file}`setup.py`. The (rare)
packages that use it will cause those dependencies to be downloaded by
setuptools directly, skipping pip's protections. If you need to use such a
package, see {ref}`Controlling setup_requires <controlling-setup-requires>`.
package, see {ref}`Controlling setup_requires <controlling-setup_requires>`.
```

View file

@ -0,0 +1,100 @@
# Secure installs
By default, pip does not perform any checks to protect against remote tampering and involves running arbitrary code from distributions. It is, however, possible to use pip in a manner that changes these behaviours, to provide a more secure installation mechanism.
This can be achieved by doing the following:
- Enable {ref}`Hash-checking mode`, by passing {any}`--require-hashes`
- Disallow source distributions, by passing {any}`--only-binary :all: <--only-binary>`
(Hash-checking mode)=
## Hash-checking Mode
```{versionadded} 8.0
```
This mode uses local hashes, embedded in a requirements.txt file, to protect against remote tampering and network issues. These hashes are specified using a `--hash` [per requirement option](per-requirement-options).
Note that hash-checking is an all-or-nothing proposition. Specifying `--hash` against _any_ requirement will activate this mode globally.
To add hashes for a package, add them to line as follows:
```
FooProject == 1.2 \
--hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 \
--hash=sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7
```
### Additional restrictions
- Hashes are required for _all_ requirements.
This is because a partially-hashed requirements file is of little use and thus likely an error: a malicious actor could slip bad code into the installation via one of the unhashed requirements.
Note that hashes embedded in URL-style requirements via the `#md5=...` syntax suffice to satisfy this rule (regardless of hash strength, for legacy reasons), though you should use a stronger hash like sha256 whenever possible.
- Hashes are required for _all_ dependencies.
If there is a dependency that is not spelled out and hashed in the requirements file, it will result in an error.
- Requirements must be pinned (either to a URL, filesystem path or using `==`).
This prevents a surprising hash mismatch upon the release of a new version that matches the requirement specifier.
### Forcing Hash-checking mode
It is possible to force the hash checking mode to be enabled, by passing `--require-hashes` command-line option.
This can be useful in deploy scripts, to ensure that the author of the requirements file provided hashes. It is also a convenient way to bootstrap your list of hashes, since it shows the hashes of the packages it fetched. It fetches only the preferred archive for each package, so you may still need to add hashes for alternatives archives using {ref}`pip hash`: for instance if there is both a binary and a source distribution.
### Hash algorithms
The recommended hash algorithm at the moment is sha256, but stronger ones are allowed, including all those supported by `hashlib`. However, weaker ones such as md5, sha1, and sha224 are excluded to avoid giving a false sense of security.
### Multiple hashes per package
It is possible to use multiple hashes for each package. This is important when a package offers binary distributions for a variety of platforms or when it is important to allow both binary and source distributions.
### Interaction with caching
The {ref}`locally-built wheel cache <wheel-caching>` is disabled in hash-checking mode to prevent spurious hash mismatch errors.
These would otherwise occur while installing sdists that had already been automatically built into cached wheels: those wheels would be selected for installation, but their hashes would not match the sdist ones from the requirements file.
A further complication is that locally built wheels are nondeterministic: contemporary modification times make their way into the archive, making hashes unpredictable across machines and cache flushes. Compilation of C code adds further nondeterminism, as many compilers include random-seeded values in their output.
However, wheels fetched from index servers are required to be the same every time. They land in pip's HTTP cache, not its wheel cache, and are used normally in hash-checking mode. The only downside of having the wheel cache disabled is thus extra build time for sdists, and this can be solved by making sure pre-built wheels are available from the index server.
### Using hashes from PyPI (or other index servers)
PyPI (and certain other index servers) provides a hash for the distribution, in the fragment portion of each download URL, like `#sha256=123...`, which pip checks as a protection against download corruption.
Other hash algorithms that have guaranteed support from `hashlib` are also supported here: sha1, sha224, sha384, sha256, and sha512. Since this hash originates remotely, it is not a useful guard against tampering and thus does not satisfy the `--require-hashes` demand that every package have a local hash.
## Repeatable installs
Hash-checking mode also works with {ref}`pip download` and {ref}`pip wheel`. See {doc}`../topics/repeatable-installs` for a comparison of hash-checking mode with other repeatability strategies.
```{warning}
Beware of the `setup_requires` keyword arg in {file}`setup.py`. The (rare) packages that use it will cause those dependencies to be downloaded by setuptools directly, skipping pip's hash-checking. If you need to use such a package, see {ref}`controlling setup_requires <controlling-setup_requires>`.
```
## Do not use setuptools directly
Be careful not to nullify all your security work by installing your actual project by using setuptools' deprecated interfaces directly: for example, by calling `python setup.py install`, `python setup.py develop`, or `easy_install`.
These will happily go out and download, unchecked, anything you missed in your requirements file and its easy to miss things as your project evolves. To be safe, install your project using pip and {any}`--no-deps`.
Instead of `python setup.py install`, use:
```{pip-cli}
$ pip install --no-deps .
```
Instead of `python setup.py develop`, use:
```{pip-cli}
$ pip install --no-deps -e .
```

View file

@ -17,9 +17,9 @@ The supported schemes are `git+file`, `git+https`, `git+ssh`, `git+http`,
`git+git` and `git`. Here are some of the supported forms:
```none
git+ssh://git.example.com/MyProject#egg=MyProject
git+file:///home/user/projects/MyProject#egg=MyProject
git+https://git.example.com/MyProject#egg=MyProject
MyProject @ git+ssh://git.example.com/MyProject
MyProject @ git+file:///home/user/projects/MyProject
MyProject @ git+https://git.example.com/MyProject
```
```{warning}
@ -34,10 +34,10 @@ It is also possible to specify a "git ref" such as branch name, a commit hash or
a tag name:
```none
git+https://git.example.com/MyProject.git@master#egg=MyProject
git+https://git.example.com/MyProject.git@v1.0#egg=MyProject
git+https://git.example.com/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709#egg=MyProject
git+https://git.example.com/MyProject.git@refs/pull/123/head#egg=MyProject
MyProject @ git+https://git.example.com/MyProject.git@master
MyProject @ git+https://git.example.com/MyProject.git@v1.0
MyProject @ git+https://git.example.com/MyProject.git@da39a3ee5e6b4b0d3255bfef95601890afd80709
MyProject @ git+https://git.example.com/MyProject.git@refs/pull/123/head
```
When passing a commit hash, specifying a full hash is preferable to a partial
@ -50,20 +50,20 @@ The supported schemes are `hg+file`, `hg+http`, `hg+https`, `hg+ssh`
and `hg+static-http`. Here are some of the supported forms:
```
hg+http://hg.myproject.org/MyProject#egg=MyProject
hg+https://hg.myproject.org/MyProject#egg=MyProject
hg+ssh://hg.myproject.org/MyProject#egg=MyProject
hg+file:///home/user/projects/MyProject#egg=MyProject
MyProject @ hg+http://hg.myproject.org/MyProject
MyProject @ hg+https://hg.myproject.org/MyProject
MyProject @ hg+ssh://hg.myproject.org/MyProject
MyProject @ hg+file:///home/user/projects/MyProject
```
It is also possible to specify a revision number, a revision hash, a tag name
or a local branch name:
```none
hg+http://hg.example.com/MyProject@da39a3ee5e6b#egg=MyProject
hg+http://hg.example.com/MyProject@2019#egg=MyProject
hg+http://hg.example.com/MyProject@v1.0#egg=MyProject
hg+http://hg.example.com/MyProject@special_feature#egg=MyProject
MyProject @ hg+http://hg.example.com/MyProject@da39a3ee5e6b
MyProject @ hg+http://hg.example.com/MyProject@2019
MyProject @ hg+http://hg.example.com/MyProject@v1.0
MyProject @ hg+http://hg.example.com/MyProject@special_feature
```
### Subversion
@ -72,16 +72,16 @@ The supported schemes are `svn`, `svn+svn`, `svn+http`, `svn+https` and
`svn+ssh`. Here are some of the supported forms:
```none
svn+https://svn.example.com/MyProject#egg=MyProject
svn+ssh://svn.example.com/MyProject#egg=MyProject
svn+ssh://user@svn.example.com/MyProject#egg=MyProject
MyProject @svn+https://svn.example.com/MyProject
MyProject @svn+ssh://svn.example.com/MyProject
MyProject @svn+ssh://user@svn.example.com/MyProject
```
You can also give specific revisions to an SVN URL, like so:
```none
-e svn+http://svn.example.com/svn/MyProject/trunk@2019#egg=MyProject
-e svn+http://svn.example.com/svn/MyProject/trunk@{20080101}#egg=MyProject
MyProject @ -e svn+http://svn.example.com/svn/MyProject/trunk@2019
MyProject @ -e svn+http://svn.example.com/svn/MyProject/trunk@{20080101}
```
Note that you need to use [Editable VCS installs](#editable-vcs-installs) for
@ -93,18 +93,18 @@ The supported schemes are `bzr+http`, `bzr+https`, `bzr+ssh`, `bzr+sftp`,
`bzr+ftp` and `bzr+lp`. Here are the supported forms:
```none
bzr+http://bzr.example.com/MyProject/trunk#egg=MyProject
bzr+sftp://user@example.com/MyProject/trunk#egg=MyProject
bzr+ssh://user@example.com/MyProject/trunk#egg=MyProject
bzr+ftp://user@example.com/MyProject/trunk#egg=MyProject
bzr+lp:MyProject#egg=MyProject
MyProject @ bzr+http://bzr.example.com/MyProject/trunk
MyProject @ bzr+sftp://user@example.com/MyProject/trunk
MyProject @ bzr+ssh://user@example.com/MyProject/trunk
MyProject @ bzr+ftp://user@example.com/MyProject/trunk
MyProject @ bzr+lp:MyProject
```
Tags or revisions can be installed like so:
```none
bzr+https://bzr.example.com/MyProject/trunk@2019#egg=MyProject
bzr+http://bzr.example.com/MyProject/trunk@v1.0#egg=MyProject
MyProject @ bzr+https://bzr.example.com/MyProject/trunk@2019
MyProject @ bzr+http://bzr.example.com/MyProject/trunk@v1.0
```
(editable-vcs-installs)=

View file

@ -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/>`_

View file

@ -1,4 +1,4 @@
sphinx ~= 4.1.0
sphinx ~= 4.2, != 4.4.0
towncrier
furo
myst_parser

View file

@ -1 +0,0 @@
Improve deprecation warning regarding the copying of source trees when installing from a local directory.

View file

@ -1,4 +0,0 @@
Add a ``feature_flag`` optional kwarg to the ``deprecated()`` function
``pip._internal.utils.deprecation:deprecated``. Also formulate a corresponding canned
message which suggests using the ``--use-feature={feature_flag}`` to test upcoming
behavior.

View file

@ -1,4 +0,0 @@
On Python 3.10 or later, the installation scheme backend has been changed to use
``sysconfig``. This is to anticipate the deprecation of ``distutils`` in Python
3.10, and its scheduled removal in 3.12. For compatibility considerations, pip
installations running on Python 3.9 or lower will continue to use ``distutils``.

View file

@ -1 +0,0 @@
Fix double unescape of HTML ``data-requires-python`` and ``data-yanked`` attributes.

View file

@ -1 +0,0 @@
Update links of setuptools as setuptools moved these documents. The Simple Repository link now points to PyPUG as that is the canonical place of packaging specification, and setuptools's ``easy_install`` is deprecated.

1
news/10696.bugfix.rst Normal file
View file

@ -0,0 +1 @@
Fix uninstall editable from Windows junction link.

2
news/10812.feature.rst Normal file
View file

@ -0,0 +1,2 @@
Improve error message when ``pip config edit`` is provided an editor that
doesn't exist.

1
news/10899.doc.rst Normal file
View file

@ -0,0 +1 @@
Add more dedicated topic and reference pages to the documentation.

View file

@ -1 +0,0 @@
When a revision is specified in a Git URL, use git's partial clone feature to speed up source retrieval.

1
news/9794.feature.rst Normal file
View file

@ -0,0 +1 @@
Validate build dependencies when using ``--no-build-isolation``.

View file

@ -1 +0,0 @@
Upgrade distro to 1.6.0.

View file

@ -65,11 +65,8 @@ def should_update_common_wheels() -> bool:
# -----------------------------------------------------------------------------
# Development Commands
# These are currently prototypes to evaluate whether we want to switch over
# completely to nox for all our automation. Contributors should prefer using
# `tox -e ...` until this note is removed.
# -----------------------------------------------------------------------------
@nox.session(python=["3.6", "3.7", "3.8", "3.9", "3.10", "pypy3"])
@nox.session(python=["3.7", "3.8", "3.9", "3.10", "pypy3"])
def test(session: nox.Session) -> None:
# Get the common wheels.
if should_update_common_wheels():
@ -113,7 +110,14 @@ def test(session: nox.Session) -> None:
# Run the tests
# LC_CTYPE is set to get UTF-8 output inside of the subprocesses that our
# tests use.
session.run("pytest", *arguments, env={"LC_CTYPE": "en_US.UTF-8"})
session.run(
"pytest",
*arguments,
env={
"LC_CTYPE": "en_US.UTF-8",
"SETUPTOOLS_USE_DISTUTILS": "stdlib",
},
)
@nox.session
@ -171,10 +175,10 @@ def lint(session: nox.Session) -> None:
@nox.session
def vendoring(session: nox.Session) -> None:
session.install("vendoring~=1.0.0")
session.install("vendoring~=1.2.0")
if "--upgrade" not in session.posargs:
session.run("vendoring", "sync", ".", "-v")
session.run("vendoring", "sync", "-v")
return
def pinned_requirements(path: Path) -> Iterator[Tuple[str, str]]:
@ -223,6 +227,21 @@ def vendoring(session: nox.Session) -> None:
release.commit_file(session, ".", message=message)
@nox.session
def coverage(session: nox.Session) -> None:
if not os.path.exists("./.coverage-output"):
os.mkdir("./.coverage-output")
session.run(
"pytest",
"--cov=pip",
"--cov-config=./setup.cfg",
env={
"COVERAGE_OUTPUT_DIR": "./.coverage-output",
"COVERAGE_PROCESS_START": "./setup.cfg",
},
)
# -----------------------------------------------------------------------------
# Release Commands
# -----------------------------------------------------------------------------

View file

@ -3,13 +3,19 @@ requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[tool.towncrier]
# For finding the __version__
package = "pip"
package_dir = "src"
# For writing into the correct file
filename = "NEWS.rst"
# For finding the news fragments
directory = "news/"
title_format = "{version} ({project_date})"
# For rendering properly for this project
issue_format = "`#{issue} <https://github.com/pypa/pip/issues/{issue}>`_"
template = "tools/news/template.rst"
# Grouping of entries, within our changelog
type = [
{ name = "Process", directory = "process", showcontent = true },
{ name = "Deprecations and Removals", directory = "removal", showcontent = true },
@ -33,6 +39,7 @@ substitute = [
# pkg_resource's vendored packages are directly vendored in pip.
{ match='pkg_resources\.extern', replace="pip._vendor" },
{ match='from \.extern', replace="from pip._vendor" },
{ match='''\('pygments\.lexers\.''', replace="('pip._vendor.pygments.lexers." },
]
drop = [
# contains unnecessary scripts
@ -44,15 +51,21 @@ drop = [
"setuptools",
"pkg_resources/_vendor/",
"pkg_resources/extern/",
# trim vendored pygments styles and lexers
"pygments/styles/[!_]*.py",
'^pygments/lexers/(?!python|__init__|_mapping).*\.py$',
# trim rich's markdown support
"rich/markdown.py",
]
[tool.vendoring.typing-stubs]
six = ["six.__init__", "six.moves.__init__", "six.moves.configparser"]
appdirs = []
distro = []
[tool.vendoring.license.directories]
setuptools = "pkg_resources"
[tool.vendoring.license.fallback-urls]
CacheControl = "https://raw.githubusercontent.com/ionrock/cachecontrol/v0.12.6/LICENSE.txt"
distlib = "https://bitbucket.org/pypa/distlib/raw/master/LICENSE.txt"
webencodings = "https://github.com/SimonSapin/python-webencodings/raw/master/LICENSE"

View file

@ -51,12 +51,9 @@ follow_imports = skip
[mypy-pip._vendor.requests.*]
follow_imports = skip
[mypy-tests.*]
# TODO: The following option should be removed at some point in the future.
allow_untyped_defs = True
[tool:pytest]
addopts = --ignore src/pip/_vendor --ignore tests/tests_cache -r aR --color=yes
xfail_strict = True
markers =
network: tests that need network
incompatible_with_sysconfig
@ -106,8 +103,6 @@ exclude_lines =
pragma: no cover
# This excludes typing-specific code, which will be validated by mypy anyway.
if TYPE_CHECKING
# Can be set to exclude e.g. `if PY2:` on Python 3
${PIP_CI_COVERAGE_EXCLUDES}
[metadata]
license_file = LICENSE.txt

View file

@ -37,7 +37,6 @@ setup(
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
@ -64,7 +63,14 @@ setup(
"pip._vendor.certifi": ["*.pem"],
"pip._vendor.requests": ["*.pem"],
"pip._vendor.distlib._backport": ["sysconfig.cfg"],
"pip._vendor.distlib": ["t32.exe", "t64.exe", "w32.exe", "w64.exe"],
"pip._vendor.distlib": [
"t32.exe",
"t64.exe",
"t64-arm.exe",
"w32.exe",
"w64.exe",
"w64-arm.exe",
],
},
entry_points={
"console_scripts": [
@ -74,5 +80,5 @@ setup(
],
},
zip_safe=False,
python_requires=">=3.6",
python_requires=">=3.7",
)

View file

@ -1,6 +1,6 @@
from typing import List, Optional
__version__ = "21.3.dev0"
__version__ = "22.1.dev0"
def main(args: Optional[List[str]] = None) -> int:

View file

@ -20,7 +20,7 @@ from pip._vendor.packaging.version import Version
from pip import __file__ as pip_location
from pip._internal.cli.spinners import open_spinner
from pip._internal.locations import get_platlib, get_prefixed_libs, get_purelib
from pip._internal.metadata import get_environment
from pip._internal.metadata import get_default_environment, get_environment
from pip._internal.utils.subprocess import call_subprocess
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
@ -93,7 +93,9 @@ class BuildEnvironment:
self._site_dir = os.path.join(temp_dir.path, "site")
if not os.path.exists(self._site_dir):
os.mkdir(self._site_dir)
with open(os.path.join(self._site_dir, "sitecustomize.py"), "w") as fp:
with open(
os.path.join(self._site_dir, "sitecustomize.py"), "w", encoding="utf-8"
) as fp:
fp.write(
textwrap.dedent(
"""
@ -166,7 +168,11 @@ class BuildEnvironment:
missing = set()
conflicting = set()
if reqs:
env = get_environment(self._lib_dirs)
env = (
get_environment(self._lib_dirs)
if hasattr(self, "_lib_dirs")
else get_default_environment()
)
for req_str in reqs:
req = Requirement(req_str)
dist = env.get_distribution(req.name)
@ -187,7 +193,8 @@ class BuildEnvironment:
finder: "PackageFinder",
requirements: Iterable[str],
prefix_as_string: str,
message: str,
*,
kind: str,
) -> None:
prefix = self._prefixes[prefix_as_string]
assert not prefix.setup
@ -195,20 +202,13 @@ class BuildEnvironment:
if not requirements:
return
with contextlib.ExitStack() as ctx:
# TODO: Remove this block when dropping 3.6 support. Python 3.6
# lacks importlib.resources and pep517 has issues loading files in
# a zip, so we fallback to the "old" method by adding the current
# pip directory to the child process's sys.path.
if sys.version_info < (3, 7):
pip_runnable = os.path.dirname(pip_location)
else:
pip_runnable = ctx.enter_context(_create_standalone_pip())
pip_runnable = ctx.enter_context(_create_standalone_pip())
self._install_requirements(
pip_runnable,
finder,
requirements,
prefix,
message,
kind=kind,
)
@staticmethod
@ -217,7 +217,8 @@ class BuildEnvironment:
finder: "PackageFinder",
requirements: Iterable[str],
prefix: _Prefix,
message: str,
*,
kind: str,
) -> None:
args: List[str] = [
sys.executable,
@ -259,8 +260,13 @@ class BuildEnvironment:
args.append("--")
args.extend(requirements)
extra_environ = {"_PIP_STANDALONE_CERT": where()}
with open_spinner(message) as spinner:
call_subprocess(args, spinner=spinner, extra_environ=extra_environ)
with open_spinner(f"Installing {kind}") as spinner:
call_subprocess(
args,
command_desc=f"pip subprocess to install {kind}",
spinner=spinner,
extra_environ=extra_environ,
)
class NoOpBuildEnvironment(BuildEnvironment):
@ -288,6 +294,7 @@ class NoOpBuildEnvironment(BuildEnvironment):
finder: "PackageFinder",
requirements: Iterable[str],
prefix_as_string: str,
message: str,
*,
kind: str,
) -> None:
raise NotImplementedError()

View file

@ -59,6 +59,14 @@ def autocomplete() -> None:
print(dist)
sys.exit(1)
should_list_installables = (
not current.startswith("-") and subcommand_name == "install"
)
if should_list_installables:
for path in auto_complete_paths(current, "path"):
print(path)
sys.exit(1)
subcommand = create_command(subcommand_name)
for opt in subcommand.parser.option_list_all:
@ -138,7 +146,7 @@ def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]:
starting with ``current``.
:param current: The word to be completed
:param completion_type: path completion type(`file`, `path` or `dir`)i
:param completion_type: path completion type(``file``, ``path`` or ``dir``)
:return: A generator of regular files and/or directories
"""
directory, filename = os.path.split(current)

View file

@ -1,5 +1,6 @@
"""Base Command class, and related routines"""
import functools
import logging
import logging.config
import optparse
@ -7,7 +8,9 @@ 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._vendor.rich import traceback as rich_traceback
from pip._internal.cli import cmdoptions
from pip._internal.cli.command_context import CommandContextMixIn
@ -21,12 +24,12 @@ from pip._internal.cli.status_codes import (
from pip._internal.exceptions import (
BadCommand,
CommandError,
DiagnosticPipError,
InstallationError,
NetworkConnectionError,
PreviousBuildDirError,
UninstallationError,
)
from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.filesystem import check_path_owner
from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
from pip._internal.utils.misc import get_prog, normalize_path
@ -148,20 +151,6 @@ class Command(CommandContextMixIn):
)
options.cache_dir = None
if getattr(options, "build_dir", None):
deprecated(
reason=(
"The -b/--build/--build-dir/--build-directory "
"option is deprecated and has no effect anymore."
),
replacement=(
"use the TMPDIR/TEMP/TMP environment variable, "
"possibly combined with --no-clean"
),
gone_in="21.3",
issue=8333,
)
if "2020-resolver" in options.features_enabled:
logger.warning(
"--use-feature=2020-resolver no longer has any effect, "
@ -169,46 +158,66 @@ 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 DiagnosticPipError as exc:
logger.error("[present-diagnostic] %s", exc)
logger.debug("Exception information:", exc_info=True)
return ERROR
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
rich_traceback.install(show_locals=True)
return run(options, args)
finally:
self.handle_pip_version_check(options)

View file

@ -10,9 +10,9 @@ pass on state. To be consistent, all options will follow this design.
# The following comment should be removed at some point in the future.
# mypy: strict-optional=False
import logging
import os
import textwrap
import warnings
from functools import partial
from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values
from textwrap import dedent
@ -30,6 +30,8 @@ from pip._internal.models.target_python import TargetPython
from pip._internal.utils.hashes import STRONG_HASHES
from pip._internal.utils.misc import strtobool
logger = logging.getLogger(__name__)
def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None:
"""
@ -76,10 +78,9 @@ def check_install_build_global(
if any(map(getname, names)):
control = options.format_control
control.disallow_binaries()
warnings.warn(
logger.warning(
"Disabling all use of wheels due to the use of --build-option "
"/ --global-option / --install-option.",
stacklevel=2,
)
@ -151,6 +152,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",
@ -165,13 +178,15 @@ isolated_mode: Callable[..., Option] = partial(
require_virtualenv: Callable[..., Option] = partial(
Option,
# Run only if inside a virtualenv, bail if not.
"--require-virtualenv",
"--require-venv",
dest="require_venv",
action="store_true",
default=False,
help=SUPPRESS_HELP,
help=(
"Allow pip to only run in a virtual environment; "
"exit with an error otherwise."
),
)
verbose: Callable[..., Option] = partial(
@ -719,18 +734,6 @@ no_deps: Callable[..., Option] = partial(
help="Don't install package dependencies.",
)
build_dir: Callable[..., Option] = partial(
PipOption,
"-b",
"--build",
"--build-dir",
"--build-directory",
dest="build_dir",
type="path",
metavar="dir",
help=SUPPRESS_HELP,
)
ignore_requires_python: Callable[..., Option] = partial(
Option,
"--ignore-requires-python",
@ -961,7 +964,12 @@ use_deprecated_feature: Callable[..., Option] = partial(
metavar="feature",
action="append",
default=[],
choices=["legacy-resolver"],
choices=[
"legacy-resolver",
"out-of-tree-build",
"backtrack-on-build-failures",
"html5lib",
],
help=("Enable deprecated functionality, that will be removed in the future."),
)
@ -974,6 +982,7 @@ general_group: Dict[str, Any] = {
"name": "General Options",
"options": [
help_,
debug_mode,
isolated_mode,
require_virtualenv,
verbose,

View file

@ -1,10 +1,23 @@
import functools
import itertools
import sys
from signal import SIGINT, default_int_handler, signal
from typing import Any
from typing import Any, Callable, Iterator, Optional, Tuple
from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar
from pip._vendor.progress.spinner import Spinner
from pip._vendor.rich.progress import (
BarColumn,
DownloadColumn,
FileSizeColumn,
Progress,
ProgressColumn,
SpinnerColumn,
TextColumn,
TimeElapsedColumn,
TimeRemainingColumn,
TransferSpeedColumn,
)
from pip._internal.utils.compat import WINDOWS
from pip._internal.utils.logging import get_indentation
@ -17,6 +30,8 @@ try:
except Exception:
colorama = None
DownloadProgressRenderer = Callable[[Iterator[bytes]], Iterator[bytes]]
def _select_progress_class(preferred: Bar, fallback: Bar) -> Bar:
encoding = getattr(preferred.file, "encoding", None)
@ -243,8 +258,64 @@ BAR_TYPES = {
}
def DownloadProgressProvider(progress_bar, max=None): # type: ignore
def _legacy_progress_bar(
progress_bar: str, max: Optional[int]
) -> DownloadProgressRenderer:
if max is None or max == 0:
return BAR_TYPES[progress_bar][1]().iter
return BAR_TYPES[progress_bar][1]().iter # type: ignore
else:
return BAR_TYPES[progress_bar][0](max=max).iter
#
# Modern replacement, for our legacy progress bars.
#
def _rich_progress_bar(
iterable: Iterator[bytes],
*,
bar_type: str,
size: int,
) -> Iterator[bytes]:
assert bar_type == "on", "This should only be used in the default mode."
if not size:
total = float("inf")
columns: Tuple[ProgressColumn, ...] = (
TextColumn("[progress.description]{task.description}"),
SpinnerColumn("line", speed=1.5),
FileSizeColumn(),
TransferSpeedColumn(),
TimeElapsedColumn(),
)
else:
total = size
columns = (
TextColumn("[progress.description]{task.description}"),
BarColumn(),
DownloadColumn(),
TransferSpeedColumn(),
TextColumn("eta"),
TimeRemainingColumn(),
)
progress = Progress(*columns, refresh_per_second=30)
task_id = progress.add_task(" " * (get_indentation() + 2), total=total)
with progress:
for chunk in iterable:
yield chunk
progress.update(task_id, advance=len(chunk))
def get_download_progress_renderer(
*, bar_type: str, size: Optional[int] = None
) -> DownloadProgressRenderer:
"""Get an object that can be used to render the download progress.
Returns a callable, that takes an iterable to "wrap".
"""
if bar_type == "on":
return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size)
elif bar_type == "off":
return iter # no-op, when passed an iterator
else:
return _legacy_progress_bar(bar_type, size)

View file

@ -34,6 +34,7 @@ from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_tracker import RequirementTracker
from pip._internal.resolution.base import BaseResolver
from pip._internal.self_outdated_check import pip_self_version_check
from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.temp_dir import (
TempDirectory,
TempDirectoryTypeRegistry,
@ -172,9 +173,10 @@ def warn_if_run_as_root() -> None:
# checks: https://mypy.readthedocs.io/en/stable/common_issues.html
if sys.platform == "win32" or sys.platform == "cygwin":
return
if sys.platform == "darwin" or sys.platform == "linux":
if os.getuid() != 0:
return
if os.getuid() != 0:
return
logger.warning(
"Running pip as the 'root' user can result in broken permissions and "
"conflicting behaviour with the system package manager. "
@ -225,6 +227,31 @@ class RequirementCommand(IndexGroupCommand):
return "2020-resolver"
@staticmethod
def determine_build_failure_suppression(options: Values) -> bool:
"""Determines whether build failures should be suppressed and backtracked on."""
if "backtrack-on-build-failures" not in options.deprecated_features_enabled:
return False
if "legacy-resolver" in options.deprecated_features_enabled:
raise CommandError("Cannot backtrack with legacy resolver.")
deprecated(
reason=(
"Backtracking on build failures can mask issues related to how "
"a package generates metadata or builds a wheel. This flag will "
"be removed in pip 22.2."
),
gone_in=None,
replacement=(
"avoiding known-bad versions by explicitly telling pip to ignore them "
"(either directly as requirements, or via a constraints file)"
),
feature_flag=None,
issue=10655,
)
return True
@classmethod
def make_requirement_preparer(
cls,
@ -235,6 +262,7 @@ class RequirementCommand(IndexGroupCommand):
finder: PackageFinder,
use_user_site: bool,
download_dir: Optional[str] = None,
verbosity: int = 0,
) -> RequirementPreparer:
"""
Create a RequirementPreparer instance for the given parameters.
@ -260,6 +288,27 @@ class RequirementCommand(IndexGroupCommand):
"fast-deps has no effect when used with the legacy resolver."
)
in_tree_build = "out-of-tree-build" not in options.deprecated_features_enabled
if "in-tree-build" in options.features_enabled:
deprecated(
reason="In-tree builds are now the default.",
replacement="to remove the --use-feature=in-tree-build flag",
gone_in="22.1",
)
if "out-of-tree-build" in options.deprecated_features_enabled:
deprecated(
reason="Out-of-tree builds are deprecated.",
replacement=None,
gone_in="22.1",
)
if options.progress_bar not in {"on", "off"}:
deprecated(
reason="Custom progress bar styles are deprecated",
replacement="to use the default progress bar style.",
gone_in="22.1",
)
return RequirementPreparer(
build_dir=temp_build_dir_path,
src_dir=options.src_dir,
@ -272,7 +321,8 @@ class RequirementCommand(IndexGroupCommand):
require_hashes=options.require_hashes,
use_user_site=use_user_site,
lazy_wheel=lazy_wheel,
in_tree_build="in-tree-build" in options.features_enabled,
verbosity=verbosity,
in_tree_build=in_tree_build,
)
@classmethod
@ -298,6 +348,7 @@ class RequirementCommand(IndexGroupCommand):
isolated=options.isolated_mode,
use_pep517=use_pep517,
)
suppress_build_failures = cls.determine_build_failure_suppression(options)
resolver_variant = cls.determine_resolver_variant(options)
# The long import name and duplicated invocation is needed to convince
# Mypy into correctly typechecking. Otherwise it would complain the
@ -317,6 +368,7 @@ class RequirementCommand(IndexGroupCommand):
force_reinstall=force_reinstall,
upgrade_strategy=upgrade_strategy,
py_version_info=py_version_info,
suppress_build_failures=suppress_build_failures,
)
import pip._internal.resolution.legacy.resolver
@ -450,4 +502,5 @@ class RequirementCommand(IndexGroupCommand):
link_collector=link_collector,
selection_prefs=selection_prefs,
target_python=target_python,
use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled,
)

View file

@ -170,12 +170,16 @@ class CacheCommand(Command):
files = self._find_wheels(options, args[0])
# Only fetch http files if no specific pattern given
no_matching_msg = "No matching packages"
if args[0] == "*":
# Only fetch http files if no specific pattern given
files += self._find_http_files(options)
else:
# Add the pattern to the log message
no_matching_msg += ' for pattern "{}"'.format(args[0])
if not files:
raise CommandError("No matching packages")
logger.warning(no_matching_msg)
for filename in files:
os.unlink(filename)

View file

@ -225,6 +225,10 @@ class ConfigurationCommand(Command):
try:
subprocess.check_call([editor, fname])
except FileNotFoundError as e:
if not e.filename:
e.filename = editor
raise
except subprocess.CalledProcessError as e:
raise PipError(
"Editor Subprocess exited with exit code {}".format(e.returncode)

View file

@ -37,7 +37,6 @@ class DownloadCommand(RequirementCommand):
def add_options(self) -> None:
self.cmd_opts.add_option(cmdoptions.constraints())
self.cmd_opts.add_option(cmdoptions.requirements())
self.cmd_opts.add_option(cmdoptions.build_dir())
self.cmd_opts.add_option(cmdoptions.no_deps())
self.cmd_opts.add_option(cmdoptions.global_options())
self.cmd_opts.add_option(cmdoptions.no_binary())
@ -114,6 +113,7 @@ class DownloadCommand(RequirementCommand):
finder=finder,
download_dir=options.download_dir,
use_user_site=False,
verbosity=self.verbosity,
)
resolver = self.make_resolver(

View file

@ -97,6 +97,7 @@ class IndexCommand(IndexGroupCommand):
link_collector=link_collector,
selection_prefs=selection_prefs,
target_python=target_python,
use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled,
)
def get_available_package_versions(self, options: Values, args: List[Any]) -> None:

View file

@ -135,8 +135,6 @@ class InstallCommand(RequirementCommand):
),
)
self.cmd_opts.add_option(cmdoptions.build_dir())
self.cmd_opts.add_option(cmdoptions.src())
self.cmd_opts.add_option(
@ -306,6 +304,12 @@ class InstallCommand(RequirementCommand):
try:
reqs = self.get_requirements(args, options, finder, session)
# Only when installing is it permitted to use PEP 660.
# In other circumstances (pip wheel, pip download) we generate
# regular (i.e. non editable) metadata and wheels.
for req in reqs:
req.permit_editable_wheels = True
reject_location_related_install_options(reqs, options.install_options)
preparer = self.make_requirement_preparer(
@ -315,6 +319,7 @@ class InstallCommand(RequirementCommand):
session=session,
finder=finder,
use_user_site=options.use_user_site,
verbosity=self.verbosity,
)
resolver = self.make_resolver(
preparer=preparer,
@ -361,22 +366,22 @@ class InstallCommand(RequirementCommand):
global_options=[],
)
# If we're using PEP 517, we cannot do a direct install
# If we're using PEP 517, we cannot do a legacy setup.py install
# so we fail here.
pep517_build_failure_names: List[str] = [
r.name for r in build_failures if r.use_pep517 # type: ignore
]
if pep517_build_failure_names:
raise InstallationError(
"Could not build wheels for {} which use"
" PEP 517 and cannot be installed directly".format(
"Could not build wheels for {}, which is required to "
"install pyproject.toml-based projects".format(
", ".join(pep517_build_failure_names)
)
)
# For now, we just warn about failures building legacy
# requirements, as we'll fall through to a direct
# install for those.
# requirements, as we'll fall through to a setup.py install for
# those.
for r in build_failures:
if not r.use_pep517:
r.legacy_install_reason = 8368

View file

@ -14,8 +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.parallel import map_multithread
from pip._internal.utils.compat import stdlib_pkgs
from pip._internal.utils.misc import tabulate, write_output
if TYPE_CHECKING:
from pip._internal.metadata.base import DistributionVersion
@ -149,6 +149,7 @@ class ListCommand(IndexGroupCommand):
return PackageFinder.create(
link_collector=link_collector,
selection_prefs=selection_prefs,
use_deprecated_html5lib="html5lib" in options.deprecated_features_enabled,
)
def run(self, options: Values, args: List[str]) -> int:
@ -253,7 +254,7 @@ class ListCommand(IndexGroupCommand):
dist.latest_filetype = typ
return dist
for dist in map_multithread(latest_info, packages):
for dist in map(latest_info, packages):
if dist is not None:
yield dist
@ -302,19 +303,22 @@ def format_for_columns(
Convert the package data into something usable
by output_package_listing_columns.
"""
running_outdated = options.outdated
# Adjust the header for the `pip list --outdated` case.
if running_outdated:
header = ["Package", "Version", "Latest", "Type"]
else:
header = ["Package", "Version"]
header = ["Package", "Version"]
data = []
if options.verbose >= 1 or any(x.editable for x in pkgs):
running_outdated = options.outdated
if running_outdated:
header.extend(["Latest", "Type"])
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)

View file

@ -1,8 +1,6 @@
import csv
import logging
import pathlib
from optparse import Values
from typing import Iterator, List, NamedTuple, Optional, Tuple
from typing import Iterator, List, NamedTuple, Optional
from pip._vendor.packaging.utils import canonicalize_name
@ -69,33 +67,6 @@ class _PackageInfo(NamedTuple):
files: Optional[List[str]]
def _convert_legacy_entry(entry: Tuple[str, ...], info: Tuple[str, ...]) -> str:
"""Convert a legacy installed-files.txt path into modern RECORD path.
The legacy format stores paths relative to the info directory, while the
modern format stores paths relative to the package root, e.g. the
site-packages directory.
:param entry: Path parts of the installed-files.txt entry.
:param info: Path parts of the egg-info directory relative to package root.
:returns: The converted entry.
For best compatibility with symlinks, this does not use ``abspath()`` or
``Path.resolve()``, but tries to work with path parts:
1. While ``entry`` starts with ``..``, remove the equal amounts of parts
from ``info``; if ``info`` is empty, start appending ``..`` instead.
2. Join the two directly.
"""
while entry and entry[0] == "..":
if not info or info[-1] == "..":
info += ("..",)
else:
info = info[:-1]
entry = entry[1:]
return str(pathlib.Path(*info, *entry))
def search_packages_info(query: List[str]) -> Iterator[_PackageInfo]:
"""
Gather details from installed distributions. Print distribution name,
@ -113,40 +84,12 @@ 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:
text = dist.read_text("RECORD")
except FileNotFoundError:
return None
# This extra Path-str cast normalizes entries.
return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines()))
def _files_from_legacy(dist: BaseDistribution) -> Optional[Iterator[str]]:
try:
text = dist.read_text("installed-files.txt")
except FileNotFoundError:
return None
paths = (p for p in text.splitlines(keepends=False) if p)
root = dist.location
info = dist.info_directory
if root is None or info is None:
return paths
try:
info_rel = pathlib.Path(info).relative_to(root)
except ValueError: # info is not relative to root.
return paths
if not info_rel.parts: # info *is* root.
return paths
return (
_convert_legacy_entry(pathlib.Path(p).parts, info_rel.parts) for p in paths
)
for query_name in query_names:
@ -155,13 +98,16 @@ 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)
except FileNotFoundError:
entry_points = []
files_iter = _files_from_record(dist) or _files_from_legacy(dist)
files_iter = dist.iter_declared_entries()
if files_iter is None:
files: Optional[List[str]] = None
else:
@ -173,8 +119,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", []),

View file

@ -65,7 +65,6 @@ class WheelCommand(RequirementCommand):
self.cmd_opts.add_option(cmdoptions.src())
self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
self.cmd_opts.add_option(cmdoptions.no_deps())
self.cmd_opts.add_option(cmdoptions.build_dir())
self.cmd_opts.add_option(cmdoptions.progress_bar())
self.cmd_opts.add_option(
@ -129,6 +128,7 @@ class WheelCommand(RequirementCommand):
finder=finder,
download_dir=options.wheel_dir,
use_user_site=False,
verbosity=self.verbosity,
)
resolver = self.make_resolver(

View file

@ -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():
@ -266,14 +266,13 @@ class Configuration:
# Doing this is useful when modifying and saving files, where we don't
# need to construct a parser.
if os.path.exists(fname):
locale_encoding = locale.getpreferredencoding(False)
try:
parser.read(fname)
parser.read(fname, encoding=locale_encoding)
except UnicodeDecodeError:
# See https://github.com/pypa/pip/issues/4963
raise ConfigurationFileCouldNotBeLoaded(
reason="contains invalid {} characters".format(
locale.getpreferredencoding(False)
),
reason=f"contains invalid {locale_encoding} characters",
fname=fname,
)
except configparser.Error as error:

View file

@ -1,5 +1,5 @@
import logging
from typing import Set, Tuple
from typing import Iterable, Set, Tuple
from pip._internal.build_env import BuildEnvironment
from pip._internal.distributions.base import AbstractDistribution
@ -19,9 +19,7 @@ class SourceDistribution(AbstractDistribution):
"""
def get_metadata_distribution(self) -> BaseDistribution:
from pip._internal.metadata.pkg_resources import Distribution as _Dist
return _Dist(self.req.get_dist())
return self.req.get_dist()
def prepare_distribution_metadata(
self, finder: PackageFinder, build_isolation: bool
@ -32,28 +30,32 @@ class SourceDistribution(AbstractDistribution):
# Set up the build isolation, if this requirement should be isolated
should_isolate = self.req.use_pep517 and build_isolation
if should_isolate:
self._setup_isolation(finder)
# Setup an isolated environment and install the build backend static
# requirements in it.
self._prepare_build_backend(finder)
# Check that if the requirement is editable, it either supports PEP 660 or
# has a setup.py or a setup.cfg. This cannot be done earlier because we need
# to setup the build backend to verify it supports build_editable, nor can
# it be done later, because we want to avoid installing build requirements
# needlessly. Doing it here also works around setuptools generating
# UNKNOWN.egg-info when running get_requires_for_build_wheel on a directory
# without setup.py nor setup.cfg.
self.req.isolated_editable_sanity_check()
# Install the dynamic build requirements.
self._install_build_reqs(finder)
elif self.req.use_pep517:
pyproject_requires = self.req.pyproject_requires
assert pyproject_requires is not None
conflicting, missing = self.req.build_env.check_requirements(
pyproject_requires
)
if conflicting:
self._raise_conflicts("the backend dependencies", conflicting)
if missing:
self._raise_missing_reqs(missing)
self.req.prepare_metadata()
def _setup_isolation(self, finder: PackageFinder) -> None:
def _raise_conflicts(
conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]]
) -> None:
format_string = (
"Some build dependencies for {requirement} "
"conflict with {conflicting_with}: {description}."
)
error_message = format_string.format(
requirement=self.req,
conflicting_with=conflicting_with,
description=", ".join(
f"{installed} is incompatible with {wanted}"
for installed, wanted in sorted(conflicting)
),
)
raise InstallationError(error_message)
def _prepare_build_backend(self, finder: PackageFinder) -> None:
# Isolate in a BuildEnvironment and install the build-time
# requirements.
pyproject_requires = self.req.pyproject_requires
@ -61,13 +63,13 @@ class SourceDistribution(AbstractDistribution):
self.req.build_env = BuildEnvironment()
self.req.build_env.install_requirements(
finder, pyproject_requires, "overlay", "Installing build dependencies"
finder, pyproject_requires, "overlay", kind="build dependencies"
)
conflicting, missing = self.req.build_env.check_requirements(
self.req.requirements_to_check
)
if conflicting:
_raise_conflicts("PEP 517/518 supported requirements", conflicting)
self._raise_conflicts("PEP 517/518 supported requirements", conflicting)
if missing:
logger.warning(
"Missing build requirements in pyproject.toml for %s.",
@ -78,19 +80,66 @@ class SourceDistribution(AbstractDistribution):
"pip cannot fall back to setuptools without %s.",
" and ".join(map(repr, sorted(missing))),
)
# Install any extra build dependencies that the backend requests.
# This must be done in a second pass, as the pyproject.toml
# dependencies must be installed before we can call the backend.
def _get_build_requires_wheel(self) -> Iterable[str]:
with self.req.build_env:
runner = runner_with_spinner_message("Getting requirements to build wheel")
backend = self.req.pep517_backend
assert backend is not None
with backend.subprocess_runner(runner):
reqs = backend.get_requires_for_build_wheel()
return backend.get_requires_for_build_wheel()
conflicting, missing = self.req.build_env.check_requirements(reqs)
def _get_build_requires_editable(self) -> Iterable[str]:
with self.req.build_env:
runner = runner_with_spinner_message(
"Getting requirements to build editable"
)
backend = self.req.pep517_backend
assert backend is not None
with backend.subprocess_runner(runner):
return backend.get_requires_for_build_editable()
def _install_build_reqs(self, finder: PackageFinder) -> None:
# Install any extra build dependencies that the backend requests.
# This must be done in a second pass, as the pyproject.toml
# dependencies must be installed before we can call the backend.
if (
self.req.editable
and self.req.permit_editable_wheels
and self.req.supports_pyproject_editable()
):
build_reqs = self._get_build_requires_editable()
else:
build_reqs = self._get_build_requires_wheel()
conflicting, missing = self.req.build_env.check_requirements(build_reqs)
if conflicting:
_raise_conflicts("the backend dependencies", conflicting)
self._raise_conflicts("the backend dependencies", conflicting)
self.req.build_env.install_requirements(
finder, missing, "normal", "Installing backend dependencies"
finder, missing, "normal", kind="backend dependencies"
)
def _raise_conflicts(
self, conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]]
) -> None:
format_string = (
"Some build dependencies for {requirement} "
"conflict with {conflicting_with}: {description}."
)
error_message = format_string.format(
requirement=self.req,
conflicting_with=conflicting_with,
description=", ".join(
f"{installed} is incompatible with {wanted}"
for installed, wanted in sorted(conflicting_reqs)
),
)
raise InstallationError(error_message)
def _raise_missing_reqs(self, missing: Set[str]) -> None:
format_string = (
"Some build dependencies for {requirement} are missing: {missing}."
)
error_message = format_string.format(
requirement=self.req, missing=", ".join(map(repr, sorted(missing)))
)
raise InstallationError(error_message)

View file

@ -1,23 +1,174 @@
"""Exceptions used throughout package"""
"""Exceptions used throughout package.
This module MUST NOT try to import from anything within `pip._internal` to
operate. This is expected to be importable from any/all files within the
subpackage and, thus, should not depend on them.
"""
import configparser
import re
from itertools import chain, groupby, repeat
from typing import TYPE_CHECKING, Dict, List, Optional, Union
from pip._vendor.pkg_resources import Distribution
from pip._vendor.requests.models import Request, Response
from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult
from pip._vendor.rich.markup import escape
from pip._vendor.rich.text import Text
if TYPE_CHECKING:
from hashlib import _Hash
from typing import Literal
from pip._internal.metadata import BaseDistribution
from pip._internal.req.req_install import InstallRequirement
#
# Scaffolding
#
def _is_kebab_case(s: str) -> bool:
return re.match(r"^[a-z]+(-[a-z]+)*$", s) is not None
def _prefix_with_indent(
s: Union[Text, str],
console: Console,
*,
prefix: str,
indent: str,
) -> Text:
if isinstance(s, Text):
text = s
else:
text = console.render_str(s)
return console.render_str(prefix, overflow="ignore") + console.render_str(
f"\n{indent}", overflow="ignore"
).join(text.split(allow_blank=True))
class PipError(Exception):
"""Base pip exception"""
"""The base pip error."""
class DiagnosticPipError(PipError):
"""An error, that presents diagnostic information to the user.
This contains a bunch of logic, to enable pretty presentation of our error
messages. Each error gets a unique reference. Each error can also include
additional context, a hint and/or a note -- which are presented with the
main error message in a consistent style.
This is adapted from the error output styling in `sphinx-theme-builder`.
"""
reference: str
def __init__(
self,
*,
kind: 'Literal["error", "warning"]' = "error",
reference: Optional[str] = None,
message: Union[str, Text],
context: Optional[Union[str, Text]],
hint_stmt: Optional[Union[str, Text]],
note_stmt: Optional[Union[str, Text]] = None,
link: Optional[str] = None,
) -> None:
# Ensure a proper reference is provided.
if reference is None:
assert hasattr(self, "reference"), "error reference not provided!"
reference = self.reference
assert _is_kebab_case(reference), "error reference must be kebab-case!"
self.kind = kind
self.reference = reference
self.message = message
self.context = context
self.note_stmt = note_stmt
self.hint_stmt = hint_stmt
self.link = link
super().__init__(f"<{self.__class__.__name__}: {self.reference}>")
def __repr__(self) -> str:
return (
f"<{self.__class__.__name__}("
f"reference={self.reference!r}, "
f"message={self.message!r}, "
f"context={self.context!r}, "
f"note_stmt={self.note_stmt!r}, "
f"hint_stmt={self.hint_stmt!r}"
")>"
)
def __rich_console__(
self,
console: Console,
options: ConsoleOptions,
) -> RenderResult:
colour = "red" if self.kind == "error" else "yellow"
yield f"[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]"
yield ""
if not options.ascii_only:
# Present the main message, with relevant context indented.
if self.context is not None:
yield _prefix_with_indent(
self.message,
console,
prefix=f"[{colour}]×[/] ",
indent=f"[{colour}]│[/] ",
)
yield _prefix_with_indent(
self.context,
console,
prefix=f"[{colour}]╰─>[/] ",
indent=f"[{colour}] [/] ",
)
else:
yield _prefix_with_indent(
self.message,
console,
prefix="[red]×[/] ",
indent=" ",
)
else:
yield self.message
if self.context is not None:
yield ""
yield self.context
if self.note_stmt is not None or self.hint_stmt is not None:
yield ""
if self.note_stmt is not None:
yield _prefix_with_indent(
self.note_stmt,
console,
prefix="[magenta bold]note[/]: ",
indent=" ",
)
if self.hint_stmt is not None:
yield _prefix_with_indent(
self.hint_stmt,
console,
prefix="[cyan bold]hint[/]: ",
indent=" ",
)
if self.link is not None:
yield ""
yield f"Link: {self.link}"
#
# Actual Errors
#
class ConfigurationError(PipError):
"""General exception in configuration"""
@ -30,18 +181,52 @@ class UninstallationError(PipError):
"""General exception during uninstallation"""
class MissingPyProjectBuildRequires(DiagnosticPipError):
"""Raised when pyproject.toml has `build-system`, but no `build-system.requires`."""
reference = "missing-pyproject-build-system-requires"
def __init__(self, *, package: str) -> None:
super().__init__(
message=f"Can not process {escape(package)}",
context=Text(
"This package has an invalid pyproject.toml file.\n"
"The [build-system] table is missing the mandatory `requires` key."
),
note_stmt="This is an issue with the package mentioned above, not pip.",
hint_stmt=Text("See PEP 518 for the detailed specification."),
)
class InvalidPyProjectBuildRequires(DiagnosticPipError):
"""Raised when pyproject.toml an invalid `build-system.requires`."""
reference = "invalid-pyproject-build-system-requires"
def __init__(self, *, package: str, reason: str) -> None:
super().__init__(
message=f"Can not process {escape(package)}",
context=Text(
"This package has an invalid `build-system.requires` key in "
f"pyproject.toml.\n{reason}"
),
note_stmt="This is an issue with the package mentioned above, not pip.",
hint_stmt=Text("See PEP 518 for the detailed specification."),
)
class NoneMetadataError(PipError):
"""
Raised when accessing "METADATA" or "PKG-INFO" metadata for a
pip._vendor.pkg_resources.Distribution object and
`dist.has_metadata('METADATA')` returns True but
`dist.get_metadata('METADATA')` returns None (and similarly for
"PKG-INFO").
"""Raised when accessing a Distribution's "METADATA" or "PKG-INFO".
This signifies an inconsistency, when the Distribution claims to have
the metadata file (if not, raise ``FileNotFoundError`` instead), but is
not actually able to produce its content. This may be due to permission
errors.
"""
def __init__(
self,
dist: Union[Distribution, "BaseDistribution"],
dist: "BaseDistribution",
metadata_name: str,
) -> None:
"""
@ -132,6 +317,17 @@ class UnsupportedWheel(InstallationError):
"""Unsupported wheel."""
class InvalidWheel(InstallationError):
"""Invalid (e.g. corrupt) wheel."""
def __init__(self, location: str, name: str):
self.location = location
self.name = name
def __str__(self) -> str:
return f"Wheel '{self.name}' located at {self.location} is invalid."
class MetadataInconsistent(InstallationError):
"""Built metadata contains inconsistent information.
@ -156,18 +352,78 @@ class MetadataInconsistent(InstallationError):
return template.format(self.ireq, self.field, self.f_val, self.m_val)
class InstallationSubprocessError(InstallationError):
"""A subprocess call failed during installation."""
class LegacyInstallFailure(DiagnosticPipError):
"""Error occurred while executing `setup.py install`"""
def __init__(self, returncode: int, description: str) -> None:
self.returncode = returncode
self.description = description
reference = "legacy-install-failure"
def __init__(self, package_details: str) -> None:
super().__init__(
message="Encountered error while trying to install package.",
context=package_details,
hint_stmt="See above for output from the failure.",
note_stmt="This is an issue with the package mentioned above, not pip.",
)
class InstallationSubprocessError(DiagnosticPipError, InstallationError):
"""A subprocess call failed."""
reference = "subprocess-exited-with-error"
def __init__(
self,
*,
command_description: str,
exit_code: int,
output_lines: Optional[List[str]],
) -> None:
if output_lines is None:
output_prompt = Text("See above for output.")
else:
output_prompt = (
Text.from_markup(f"[red][{len(output_lines)} lines of output][/]\n")
+ Text("".join(output_lines))
+ Text.from_markup(R"[red]\[end of output][/]")
)
super().__init__(
message=(
f"[green]{escape(command_description)}[/] did not run successfully.\n"
f"exit code: {exit_code}"
),
context=output_prompt,
hint_stmt=None,
note_stmt=(
"This error originates from a subprocess, and is likely not a "
"problem with pip."
),
)
self.command_description = command_description
self.exit_code = exit_code
def __str__(self) -> str:
return (
"Command errored out with exit status {}: {} "
"Check the logs for full command output."
).format(self.returncode, self.description)
return f"{self.command_description} exited with {self.exit_code}"
class MetadataGenerationFailed(InstallationSubprocessError, InstallationError):
reference = "metadata-generation-failed"
def __init__(
self,
*,
package_details: str,
) -> None:
super(InstallationSubprocessError, self).__init__(
message="Encountered error while generating package metadata.",
context=escape(package_details),
hint_stmt="See above for details.",
note_stmt="This is an issue with the package mentioned above, not pip.",
)
def __str__(self) -> str:
return "metadata generation failed"
class HashErrors(InstallationError):

Some files were not shown because too many files have changed in this diff Show more