mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge branch 'main' into extra-normalization
This commit is contained in:
commit
0db5d95da0
195 changed files with 5695 additions and 2624 deletions
36
.git-blame-ignore-revs
Normal file
36
.git-blame-ignore-revs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
917b41d6d73535c090fc312668dff353cdaef906 # Blacken docs/html/conf.py
|
||||||
|
ed383dd8afa8fe0250dcf9b8962927ada0e21c89 # Blacken docs/pip_sphinxext.py
|
||||||
|
228405e62451abe8a66233573035007df4be575f # Blacken noxfile.py
|
||||||
|
f477a9f490e978177b71c9dbaa5465c51ea21129 # Blacken setup.py
|
||||||
|
e59ba23468390217479465019f8d78e724a23550 # Blacken src/pip/__main__.py
|
||||||
|
d7013db084e9a52242354ee5754dc5d19ccf062e # Blacken src/pip/_internal/build_env.py
|
||||||
|
30e9ffacae75378fc3e3df48f754dabad037edb9 # Blacken src/pip/_internal/cache.py
|
||||||
|
8341d56b46776a805286218ac5fb0e7850fd9341 # Blacken src/pip/_internal/cli/autocompletion.py
|
||||||
|
3d3461ed65208656358b3595e25d8c31c5c89470 # Blacken src/pip/_internal/cli/base_command.py
|
||||||
|
d489b0f1b104bc936b0fb17e6c33633664ebdc0e # Blacken src/pip/_internal/cli/cmdoptions.py
|
||||||
|
591fe4841aefe9befa0530f2a54f820c4ecbb392 # Blacken src/pip/_internal/cli/command_context.py
|
||||||
|
9265b28ef7248ae1847a80384dbeeb8119c3e2f5 # Blacken src/pip/_internal/cli/main.py
|
||||||
|
847a369364878c38d210c90beed2737bb6fb3a85 # Blacken src/pip/_internal/cli/main_parser.py
|
||||||
|
ec97119067041ae58b963935ff5f0e5d9fead80c # Blacken src/pip/_internal/cli/parser.py
|
||||||
|
6e3b8de22fa39fa3073599ecf9db61367f4b3b32 # Blacken src/pip/_internal/cli/progress_bars.py
|
||||||
|
55405227de983c5bd5bf0858ea12dbe537d3e490 # Blacken src/pip/_internal/cli/req_command.py
|
||||||
|
d5ca5c850cae9a0c64882a8f49d3a318699a7e2e # Blacken src/pip/_internal/cli/spinners.py
|
||||||
|
9747cb48f8430a7a91b36fe697dd18dbddb319f0 # Blacken src/pip/_internal/commands/__init__.py
|
||||||
|
1c09fd6f124df08ca36bed68085ad68e89bb1957 # Blacken src/pip/_internal/commands/cache.py
|
||||||
|
315e93d7eb87cd476afcc4eaf0f01a7b56a5037f # Blacken src/pip/_internal/commands/check.py
|
||||||
|
8ae3b96ed7d24fd24024ccce4840da0dcf635f26 # Blacken src/pip/_internal/commands/completion.py
|
||||||
|
42ca4792202f26a293ee48380718743a80bbee37 # Blacken src/pip/_internal/commands/configuration.py
|
||||||
|
790ad78fcd43d41a5bef9dca34a3c128d05eb02c # Blacken src/pip/_internal/commands/debug.py
|
||||||
|
a6fcc8f045afe257ce321f4012fc8fcb4be01eb3 # Blacken src/pip/_internal/commands/download.py
|
||||||
|
920e735dfc60109351fbe2f4c483c2f6ede9e52d # Blacken src/pip/_internal/commands/freeze.py
|
||||||
|
053004e0fcf0851238b1064fbce13aea87b24e9c # Blacken src/pip/_internal/commands/hash.py
|
||||||
|
a6b6ae487e52c2242045b64cb8962e0a992cfd76 # Blacken src/pip/_internal/commands/help.py
|
||||||
|
2495cf95a6c7eb61ccf1f9f0e8b8d736af914e53 # Blacken __main__.py
|
||||||
|
c7ee560e00b85f7486b452c14ff49e4737996eda # Blacken tools/
|
||||||
|
8e2e1964a4f0a060f7299a96a911c9e116b2283d # Blacken src/pip/_internal/commands/
|
||||||
|
1bc0eef05679e87f45540ab0a294667cb3c6a88e # Blacken src/pip/_internal/network/
|
||||||
|
069b01932a7d64a81c708c6254cc93e1f89e6783 # Blacken src/pip/_internal/req
|
||||||
|
1897784d59e0d5fcda2dd75fea54ddd8be3d502a # Blacken src/pip/_internal/index
|
||||||
|
94999255d5ede440c37137d210666fdf64302e75 # Reformat the codebase, with black
|
||||||
|
585037a80a1177f1fa92e159a7079855782e543e # Cleanup implicit string concatenation
|
||||||
|
8a6f6ac19b80a6dc35900a47016c851d9fcd2ee2 # Blacken src/pip/_internal/resolution directory
|
35
.github/workflows/ci.yml
vendored
35
.github/workflows/ci.yml
vendored
|
@ -91,7 +91,7 @@ jobs:
|
||||||
- run: git diff --exit-code
|
- run: git diff --exit-code
|
||||||
|
|
||||||
tests-unix:
|
tests-unix:
|
||||||
name: tests / ${{ matrix.python }} / ${{ matrix.os }}
|
name: tests / ${{ matrix.python.key || matrix.python }} / ${{ matrix.os }}
|
||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
|
||||||
needs: [packaging, determine-changes]
|
needs: [packaging, determine-changes]
|
||||||
|
@ -109,12 +109,14 @@ jobs:
|
||||||
- "3.9"
|
- "3.9"
|
||||||
- "3.10"
|
- "3.10"
|
||||||
- "3.11"
|
- "3.11"
|
||||||
|
- "3.12"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
allow-prereleases: true
|
||||||
|
|
||||||
- name: Install Ubuntu dependencies
|
- name: Install Ubuntu dependencies
|
||||||
if: matrix.os == 'Ubuntu'
|
if: matrix.os == 'Ubuntu'
|
||||||
|
@ -129,12 +131,12 @@ jobs:
|
||||||
# Main check
|
# Main check
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: >-
|
run: >-
|
||||||
nox -s test-${{ matrix.python }} --
|
nox -s test-${{ matrix.python.key || matrix.python }} --
|
||||||
-m unit
|
-m unit
|
||||||
--verbose --numprocesses auto --showlocals
|
--verbose --numprocesses auto --showlocals
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
run: >-
|
run: >-
|
||||||
nox -s test-${{ matrix.python }} --
|
nox -s test-${{ matrix.python.key || matrix.python }} --
|
||||||
-m integration
|
-m integration
|
||||||
--verbose --numprocesses auto --showlocals
|
--verbose --numprocesses auto --showlocals
|
||||||
--durations=5
|
--durations=5
|
||||||
|
@ -167,24 +169,13 @@ jobs:
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
|
||||||
# We use a RAMDisk on Windows, since filesystem IO is a big slowdown
|
# We use C:\Temp (which is already available on the worker)
|
||||||
# for our tests.
|
# as a temporary directory for all of the tests because the
|
||||||
- name: Create a RAMDisk
|
# default value (under the user dir) is more deeply nested
|
||||||
run: ./tools/ci/New-RAMDisk.ps1 -Drive R -Size 1GB
|
# and causes tests to fail with "path too long" errors.
|
||||||
|
|
||||||
- name: Setup RAMDisk permissions
|
|
||||||
run: |
|
|
||||||
mkdir R:\Temp
|
|
||||||
$acl = Get-Acl "R:\Temp"
|
|
||||||
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
||||||
"Everyone", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
|
|
||||||
)
|
|
||||||
$acl.AddAccessRule($rule)
|
|
||||||
Set-Acl "R:\Temp" $acl
|
|
||||||
|
|
||||||
- run: pip install nox
|
- run: pip install nox
|
||||||
env:
|
env:
|
||||||
TEMP: "R:\\Temp"
|
TEMP: "C:\\Temp"
|
||||||
|
|
||||||
# Main check
|
# Main check
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
|
@ -194,7 +185,7 @@ jobs:
|
||||||
-m unit
|
-m unit
|
||||||
--verbose --numprocesses auto --showlocals
|
--verbose --numprocesses auto --showlocals
|
||||||
env:
|
env:
|
||||||
TEMP: "R:\\Temp"
|
TEMP: "C:\\Temp"
|
||||||
|
|
||||||
- name: Run integration tests (group 1)
|
- name: Run integration tests (group 1)
|
||||||
if: matrix.group == 1
|
if: matrix.group == 1
|
||||||
|
@ -203,7 +194,7 @@ jobs:
|
||||||
-m integration -k "not test_install"
|
-m integration -k "not test_install"
|
||||||
--verbose --numprocesses auto --showlocals
|
--verbose --numprocesses auto --showlocals
|
||||||
env:
|
env:
|
||||||
TEMP: "R:\\Temp"
|
TEMP: "C:\\Temp"
|
||||||
|
|
||||||
- name: Run integration tests (group 2)
|
- name: Run integration tests (group 2)
|
||||||
if: matrix.group == 2
|
if: matrix.group == 2
|
||||||
|
@ -212,7 +203,7 @@ jobs:
|
||||||
-m integration -k "test_install"
|
-m integration -k "test_install"
|
||||||
--verbose --numprocesses auto --showlocals
|
--verbose --numprocesses auto --showlocals
|
||||||
env:
|
env:
|
||||||
TEMP: "R:\\Temp"
|
TEMP: "C:\\Temp"
|
||||||
|
|
||||||
tests-zipapp:
|
tests-zipapp:
|
||||||
name: tests / zipapp
|
name: tests / zipapp
|
||||||
|
|
19
.github/workflows/no-response.yml
vendored
19
.github/workflows/no-response.yml
vendored
|
@ -1,19 +0,0 @@
|
||||||
name: No Response
|
|
||||||
|
|
||||||
# Both `issue_comment` and `scheduled` event types are required for this Action
|
|
||||||
# to work properly.
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
schedule:
|
|
||||||
# Schedule for five minutes after the hour, every hour
|
|
||||||
- cron: '5 * * * *'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
noResponse:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: lee-dohm/no-response@v0.5.0
|
|
||||||
with:
|
|
||||||
token: ${{ github.token }}
|
|
||||||
responseRequiredLabel: "S: awaiting response"
|
|
|
@ -17,26 +17,15 @@ repos:
|
||||||
exclude: .patch
|
exclude: .patch
|
||||||
|
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 23.1.0
|
rev: 23.7.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 6.0.0
|
# Ruff version.
|
||||||
|
rev: v0.0.287
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: ruff
|
||||||
additional_dependencies: [
|
|
||||||
'flake8-bugbear',
|
|
||||||
'flake8-logging-format',
|
|
||||||
'flake8-implicit-str-concat',
|
|
||||||
]
|
|
||||||
exclude: tests/data
|
|
||||||
|
|
||||||
- repo: https://github.com/PyCQA/isort
|
|
||||||
rev: 5.12.0
|
|
||||||
hooks:
|
|
||||||
- id: isort
|
|
||||||
files: \.py$
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v0.961
|
rev: v0.961
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
|
build:
|
||||||
|
os: ubuntu-22.04
|
||||||
|
tools:
|
||||||
|
python: "3.11"
|
||||||
|
|
||||||
sphinx:
|
sphinx:
|
||||||
builder: htmldir
|
builder: dirhtml
|
||||||
configuration: docs/html/conf.py
|
configuration: docs/html/conf.py
|
||||||
|
|
||||||
python:
|
python:
|
||||||
version: 3.8
|
|
||||||
install:
|
install:
|
||||||
- requirements: docs/requirements.txt
|
- requirements: docs/requirements.txt
|
||||||
|
|
10
AUTHORS.txt
10
AUTHORS.txt
|
@ -71,6 +71,7 @@ atse
|
||||||
Atsushi Odagiri
|
Atsushi Odagiri
|
||||||
Avinash Karhana
|
Avinash Karhana
|
||||||
Avner Cohen
|
Avner Cohen
|
||||||
|
Awit (Ah-Wit) Ghirmai
|
||||||
Baptiste Mispelon
|
Baptiste Mispelon
|
||||||
Barney Gale
|
Barney Gale
|
||||||
barneygale
|
barneygale
|
||||||
|
@ -126,6 +127,7 @@ Chih-Hsuan Yen
|
||||||
Chris Brinker
|
Chris Brinker
|
||||||
Chris Hunt
|
Chris Hunt
|
||||||
Chris Jerdonek
|
Chris Jerdonek
|
||||||
|
Chris Kuehl
|
||||||
Chris McDonough
|
Chris McDonough
|
||||||
Chris Pawley
|
Chris Pawley
|
||||||
Chris Pryer
|
Chris Pryer
|
||||||
|
@ -330,6 +332,8 @@ Jarek Potiuk
|
||||||
jarondl
|
jarondl
|
||||||
Jason Curtis
|
Jason Curtis
|
||||||
Jason R. Coombs
|
Jason R. Coombs
|
||||||
|
JasonMo
|
||||||
|
JasonMo1
|
||||||
Jay Graves
|
Jay Graves
|
||||||
Jean-Christophe Fillion-Robin
|
Jean-Christophe Fillion-Robin
|
||||||
Jeff Barber
|
Jeff Barber
|
||||||
|
@ -344,6 +348,7 @@ Jim Fisher
|
||||||
Jim Garrison
|
Jim Garrison
|
||||||
Jiun Bae
|
Jiun Bae
|
||||||
Jivan Amara
|
Jivan Amara
|
||||||
|
Joe Bylund
|
||||||
Joe Michelini
|
Joe Michelini
|
||||||
John Paton
|
John Paton
|
||||||
John T. Wodder II
|
John T. Wodder II
|
||||||
|
@ -441,6 +446,7 @@ Matthew Einhorn
|
||||||
Matthew Feickert
|
Matthew Feickert
|
||||||
Matthew Gilliard
|
Matthew Gilliard
|
||||||
Matthew Iversen
|
Matthew Iversen
|
||||||
|
Matthew Treinish
|
||||||
Matthew Trumbell
|
Matthew Trumbell
|
||||||
Matthew Willson
|
Matthew Willson
|
||||||
Matthias Bussonnier
|
Matthias Bussonnier
|
||||||
|
@ -582,6 +588,7 @@ Rishi
|
||||||
RobberPhex
|
RobberPhex
|
||||||
Robert Collins
|
Robert Collins
|
||||||
Robert McGibbon
|
Robert McGibbon
|
||||||
|
Robert Pollak
|
||||||
Robert T. McGibbon
|
Robert T. McGibbon
|
||||||
robin elisha robinson
|
robin elisha robinson
|
||||||
Roey Berman
|
Roey Berman
|
||||||
|
@ -614,6 +621,7 @@ SeongSoo Cho
|
||||||
Sergey Vasilyev
|
Sergey Vasilyev
|
||||||
Seth Michael Larson
|
Seth Michael Larson
|
||||||
Seth Woodworth
|
Seth Woodworth
|
||||||
|
Shantanu
|
||||||
shireenrao
|
shireenrao
|
||||||
Shivansh-007
|
Shivansh-007
|
||||||
Shlomi Fish
|
Shlomi Fish
|
||||||
|
@ -638,6 +646,7 @@ Steve Barnes
|
||||||
Steve Dower
|
Steve Dower
|
||||||
Steve Kowalik
|
Steve Kowalik
|
||||||
Steven Myint
|
Steven Myint
|
||||||
|
Steven Silvester
|
||||||
stonebig
|
stonebig
|
||||||
Stéphane Bidoul
|
Stéphane Bidoul
|
||||||
Stéphane Bidoul (ACSONE)
|
Stéphane Bidoul (ACSONE)
|
||||||
|
@ -707,6 +716,7 @@ Wilson Mo
|
||||||
wim glenn
|
wim glenn
|
||||||
Winson Luk
|
Winson Luk
|
||||||
Wolfgang Maier
|
Wolfgang Maier
|
||||||
|
Wu Zhenyu
|
||||||
XAMES3
|
XAMES3
|
||||||
Xavier Fernandez
|
Xavier Fernandez
|
||||||
xoviat
|
xoviat
|
||||||
|
|
|
@ -14,6 +14,7 @@ recursive-include src/pip/_vendor *COPYING*
|
||||||
include docs/docutils.conf
|
include docs/docutils.conf
|
||||||
include docs/requirements.txt
|
include docs/requirements.txt
|
||||||
|
|
||||||
|
exclude .git-blame-ignore-revs
|
||||||
exclude .coveragerc
|
exclude .coveragerc
|
||||||
exclude .mailmap
|
exclude .mailmap
|
||||||
exclude .appveyor.yml
|
exclude .appveyor.yml
|
||||||
|
|
65
NEWS.rst
65
NEWS.rst
|
@ -9,6 +9,69 @@
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
23.2.1 (2023-07-22)
|
||||||
|
===================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Disable PEP 658 metadata fetching with the legacy resolver. (`#12156 <https://github.com/pypa/pip/issues/12156>`_)
|
||||||
|
|
||||||
|
|
||||||
|
23.2 (2023-07-15)
|
||||||
|
=================
|
||||||
|
|
||||||
|
Process
|
||||||
|
-------
|
||||||
|
|
||||||
|
- Deprecate support for eggs for Python 3.11 or later, when the new ``importlib.metadata`` backend is used to load distribution metadata. This only affects the egg *distribution format* (with the ``.egg`` extension); distributions using the ``.egg-info`` *metadata format* (but are not actually eggs) are not affected. For more information about eggs, see `relevant section in the setuptools documentation <https://setuptools.pypa.io/en/stable/deprecated/python_eggs.html>`__.
|
||||||
|
|
||||||
|
Deprecations and Removals
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- Deprecate legacy version and version specifiers that don't conform to `PEP 440
|
||||||
|
<https://peps.python.org/pep-0440/>`_ (`#12063 <https://github.com/pypa/pip/issues/12063>`_)
|
||||||
|
- ``freeze`` no longer excludes the ``setuptools``, ``distribute``, and ``wheel``
|
||||||
|
from the output when running on Python 3.12 or later, where they are not
|
||||||
|
included in a virtual environment by default. Use ``--exclude`` if you wish to
|
||||||
|
exclude any of these packages. (`#4256 <https://github.com/pypa/pip/issues/4256>`_)
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- make rejection messages slightly different between 1 and 8, so the user can make the difference. (`#12040 <https://github.com/pypa/pip/issues/12040>`_)
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Fix ``pip completion --zsh``. (`#11417 <https://github.com/pypa/pip/issues/11417>`_)
|
||||||
|
- Prevent downloading files twice when PEP 658 metadata is present (`#11847 <https://github.com/pypa/pip/issues/11847>`_)
|
||||||
|
- Add permission check before configuration (`#11920 <https://github.com/pypa/pip/issues/11920>`_)
|
||||||
|
- Fix deprecation warnings in Python 3.12 for usage of shutil.rmtree (`#11957 <https://github.com/pypa/pip/issues/11957>`_)
|
||||||
|
- Ignore invalid or unreadable ``origin.json`` files in the cache of locally built wheels. (`#11985 <https://github.com/pypa/pip/issues/11985>`_)
|
||||||
|
- Fix installation of packages with PEP658 metadata using non-canonicalized names (`#12038 <https://github.com/pypa/pip/issues/12038>`_)
|
||||||
|
- Correctly parse ``dist-info-metadata`` values from JSON-format index data. (`#12042 <https://github.com/pypa/pip/issues/12042>`_)
|
||||||
|
- Fail with an error if the ``--python`` option is specified after the subcommand name. (`#12067 <https://github.com/pypa/pip/issues/12067>`_)
|
||||||
|
- Fix slowness when using ``importlib.metadata`` (the default way for pip to read metadata in Python 3.11+) and there is a large overlap between already installed and to-be-installed packages. (`#12079 <https://github.com/pypa/pip/issues/12079>`_)
|
||||||
|
- Pass the ``-r`` flag to mercurial to be explicit that a revision is passed and protect
|
||||||
|
against ``hg`` options injection as part of VCS URLs. Users that do not have control on
|
||||||
|
VCS URLs passed to pip are advised to upgrade. (`#12119 <https://github.com/pypa/pip/issues/12119>`_)
|
||||||
|
|
||||||
|
Vendored Libraries
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Upgrade certifi to 2023.5.7
|
||||||
|
- Upgrade platformdirs to 3.8.1
|
||||||
|
- Upgrade pygments to 2.15.1
|
||||||
|
- Upgrade pyparsing to 3.1.0
|
||||||
|
- Upgrade Requests to 2.31.0
|
||||||
|
- Upgrade rich to 13.4.2
|
||||||
|
- Upgrade setuptools to 68.0.0
|
||||||
|
- Updated typing_extensions to 4.6.0
|
||||||
|
- Upgrade typing_extensions to 4.7.1
|
||||||
|
- Upgrade urllib3 to 1.26.16
|
||||||
|
|
||||||
|
|
||||||
23.1.2 (2023-04-26)
|
23.1.2 (2023-04-26)
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
@ -53,7 +116,7 @@ Deprecations and Removals
|
||||||
``--config-settings``. (`#11859 <https://github.com/pypa/pip/issues/11859>`_)
|
``--config-settings``. (`#11859 <https://github.com/pypa/pip/issues/11859>`_)
|
||||||
- Using ``--config-settings`` with projects that don't have a ``pyproject.toml`` now prints
|
- Using ``--config-settings`` with projects that don't have a ``pyproject.toml`` now prints
|
||||||
a deprecation warning. In the future the presence of config settings will automatically
|
a deprecation warning. In the future the presence of config settings will automatically
|
||||||
enable the default build backend for legacy projects and pass the setttings to it. (`#11915 <https://github.com/pypa/pip/issues/11915>`_)
|
enable the default build backend for legacy projects and pass the settings to it. (`#11915 <https://github.com/pypa/pip/issues/11915>`_)
|
||||||
- Remove ``setup.py install`` fallback when building a wheel failed for projects without
|
- Remove ``setup.py install`` fallback when building a wheel failed for projects without
|
||||||
``pyproject.toml``. (`#8368 <https://github.com/pypa/pip/issues/8368>`_)
|
``pyproject.toml``. (`#8368 <https://github.com/pypa/pip/issues/8368>`_)
|
||||||
- When the ``wheel`` package is not installed, pip now uses the default build backend
|
- When the ``wheel`` package is not installed, pip now uses the default build backend
|
||||||
|
|
|
@ -19,8 +19,6 @@ We release updates regularly, with a new version every 3 months. Find more detai
|
||||||
* `Release notes`_
|
* `Release notes`_
|
||||||
* `Release process`_
|
* `Release process`_
|
||||||
|
|
||||||
In pip 20.3, we've `made a big improvement to the heart of pip`_; `learn more`_. We want your input, so `sign up for our user experience research studies`_ to help us do it right.
|
|
||||||
|
|
||||||
**Note**: pip 21.0, in January 2021, removed Python 2 support, per pip's `Python 2 support policy`_. Please migrate to Python 3.
|
**Note**: pip 21.0, in January 2021, removed Python 2 support, per pip's `Python 2 support policy`_. Please migrate to Python 3.
|
||||||
|
|
||||||
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
|
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
|
||||||
|
@ -49,9 +47,6 @@ rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
|
||||||
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
|
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
|
||||||
.. _GitHub page: https://github.com/pypa/pip
|
.. _GitHub page: https://github.com/pypa/pip
|
||||||
.. _Development documentation: https://pip.pypa.io/en/latest/development
|
.. _Development documentation: https://pip.pypa.io/en/latest/development
|
||||||
.. _made a big improvement to the heart of pip: https://pyfound.blogspot.com/2020/11/pip-20-3-new-resolver.html
|
|
||||||
.. _learn more: https://pip.pypa.io/en/latest/user_guide/#changes-to-the-pip-dependency-resolver-in-20-3-2020
|
|
||||||
.. _sign up for our user experience research studies: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html
|
|
||||||
.. _Python 2 support policy: https://pip.pypa.io/en/latest/development/release-process/#python-2-support
|
.. _Python 2 support policy: https://pip.pypa.io/en/latest/development/release-process/#python-2-support
|
||||||
.. _Issue tracking: https://github.com/pypa/pip/issues
|
.. _Issue tracking: https://github.com/pypa/pip/issues
|
||||||
.. _Discourse channel: https://discuss.python.org/c/packaging
|
.. _Discourse channel: https://discuss.python.org/c/packaging
|
||||||
|
|
11
SECURITY.md
11
SECURITY.md
|
@ -1,3 +1,10 @@
|
||||||
# Security and Vulnerability Reporting
|
# Security Policy
|
||||||
|
|
||||||
If you find any security issues, please report to [security@python.org](mailto:security@python.org)
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Please read the guidelines on reporting security issues [on the
|
||||||
|
official website](https://www.python.org/dev/security/) for
|
||||||
|
instructions on how to report a security-related problem to
|
||||||
|
the Python Security Response Team responsibly.
|
||||||
|
|
||||||
|
To reach the response team, email `security at python dot org`.
|
||||||
|
|
|
@ -21,6 +21,12 @@ Usage
|
||||||
Description
|
Description
|
||||||
===========
|
===========
|
||||||
|
|
||||||
|
.. attention::
|
||||||
|
PyPI no longer supports ``pip search`` (or XML-RPC search). Please use https://pypi.org/search (via a browser)
|
||||||
|
instead. See https://warehouse.pypa.io/api-reference/xml-rpc.html#deprecated-methods for more information.
|
||||||
|
|
||||||
|
However, XML-RPC search (and this command) may still be supported by indexes other than PyPI.
|
||||||
|
|
||||||
.. pip-command-description:: search
|
.. pip-command-description:: search
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,16 +27,7 @@ Development Environment
|
||||||
pip is a command line application written in Python. For developing pip,
|
pip is a command line application written in Python. For developing pip,
|
||||||
you should `install Python`_ on your computer.
|
you should `install Python`_ on your computer.
|
||||||
|
|
||||||
For developing pip, you need to install :pypi:`nox`. Often, you can run
|
For developing pip, you need to install :pypi:`nox`. The full development setup would then be:
|
||||||
``python -m pip install nox`` to install and use it.
|
|
||||||
|
|
||||||
|
|
||||||
Running pip From Source Tree
|
|
||||||
============================
|
|
||||||
|
|
||||||
To run the pip executable from your source tree during development, install pip
|
|
||||||
locally using editable installation (inside a virtualenv).
|
|
||||||
You can then invoke your local source tree pip normally.
|
|
||||||
|
|
||||||
.. tab:: Unix/macOS
|
.. tab:: Unix/macOS
|
||||||
|
|
||||||
|
@ -44,8 +35,7 @@ You can then invoke your local source tree pip normally.
|
||||||
|
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
python -m pip install -e .
|
python -m pip install nox
|
||||||
python -m pip --version
|
|
||||||
|
|
||||||
.. tab:: Windows
|
.. tab:: Windows
|
||||||
|
|
||||||
|
@ -53,6 +43,26 @@ You can then invoke your local source tree pip normally.
|
||||||
|
|
||||||
py -m venv .venv
|
py -m venv .venv
|
||||||
.venv\Scripts\activate
|
.venv\Scripts\activate
|
||||||
|
py -m pip install nox
|
||||||
|
|
||||||
|
Running pip From Source Tree
|
||||||
|
============================
|
||||||
|
|
||||||
|
To run the pip executable from your source tree during development, install pip
|
||||||
|
locally using editable installation (inside a virtualenv).
|
||||||
|
You can then invoke your local source tree pip normally (be sure virtualenv is active).
|
||||||
|
|
||||||
|
.. tab:: Unix/macOS
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
python -m pip install -e .
|
||||||
|
python -m pip --version
|
||||||
|
|
||||||
|
.. tab:: Windows
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
py -m pip install -e .
|
py -m pip install -e .
|
||||||
py -m pip --version
|
py -m pip --version
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ $ pip install --upgrade pip
|
||||||
The current version of pip works on:
|
The current version of pip works on:
|
||||||
|
|
||||||
- Windows, Linux and MacOS.
|
- Windows, Linux and MacOS.
|
||||||
- CPython 3.7, 3.8, 3.9, 3.10 and latest PyPy3.
|
- CPython 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, and latest PyPy3.
|
||||||
|
|
||||||
pip is tested to work on the latest patch version of the Python interpreter,
|
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
|
for each of the minor versions listed above. Previous patch versions are
|
||||||
|
|
|
@ -56,6 +56,9 @@ package with the following properties:
|
||||||
URL reference. `false` if the requirements was provided as a name and version
|
URL reference. `false` if the requirements was provided as a name and version
|
||||||
specifier.
|
specifier.
|
||||||
|
|
||||||
|
- `is_yanked`: `true` if the requirement was yanked from the index, but was still
|
||||||
|
selected by pip conform to [PEP 592](https://peps.python.org/pep-0592/#installers).
|
||||||
|
|
||||||
- `download_info`: Information about the artifact (to be) downloaded for installation,
|
- `download_info`: Information about the artifact (to be) downloaded for installation,
|
||||||
using the [direct URL data
|
using the [direct URL data
|
||||||
structure](https://packaging.python.org/en/latest/specifications/direct-url-data-structure/).
|
structure](https://packaging.python.org/en/latest/specifications/direct-url-data-structure/).
|
||||||
|
@ -106,6 +109,7 @@ will produce an output similar to this (metadata abriged for brevity):
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is_direct": false,
|
"is_direct": false,
|
||||||
|
"is_yanked": false,
|
||||||
"requested": true,
|
"requested": true,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "pydantic",
|
"name": "pydantic",
|
||||||
|
@ -133,6 +137,7 @@ will produce an output similar to this (metadata abriged for brevity):
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is_direct": true,
|
"is_direct": true,
|
||||||
|
"is_yanked": false,
|
||||||
"requested": true,
|
"requested": true,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "packaging",
|
"name": "packaging",
|
||||||
|
|
|
@ -68,7 +68,7 @@ man pages][netrc-docs].
|
||||||
pip supports loading credentials stored in your keyring using the
|
pip supports loading credentials stored in your keyring using the
|
||||||
{pypi}`keyring` library, which can be enabled py passing `--keyring-provider`
|
{pypi}`keyring` library, which can be enabled py passing `--keyring-provider`
|
||||||
with a value of `auto`, `disabled`, `import`, or `subprocess`. The default
|
with a value of `auto`, `disabled`, `import`, or `subprocess`. The default
|
||||||
value `auto` respects `--no-input` and not query keyring at all if the option
|
value `auto` respects `--no-input` and does not query keyring at all if the option
|
||||||
is used; otherwise it tries the `import`, `subprocess`, and `disabled`
|
is used; otherwise it tries the `import`, `subprocess`, and `disabled`
|
||||||
providers (in this order) and uses the first one that works.
|
providers (in this order) and uses the first one that works.
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ and this article is intended to help readers understand what is happening
|
||||||
```{note}
|
```{note}
|
||||||
This document is a work in progress. The details included are accurate (at the
|
This document is a work in progress. The details included are accurate (at the
|
||||||
time of writing), but there is additional information, in particular around
|
time of writing), but there is additional information, in particular around
|
||||||
pip's interface with resolvelib, which have not yet been included.
|
pip's interface with resolvelib, which has not yet been included.
|
||||||
|
|
||||||
Contributions to improve this document are welcome.
|
Contributions to improve this document are welcome.
|
||||||
```
|
```
|
||||||
|
@ -26,7 +26,7 @@ The practical implication of that is that there will always be some situations
|
||||||
where pip cannot determine what to install in a reasonable length of time. We
|
where pip cannot determine what to install in a reasonable length of time. We
|
||||||
make every effort to ensure that such situations happen rarely, but eliminating
|
make every effort to ensure that such situations happen rarely, but eliminating
|
||||||
them altogether isn't even theoretically possible. We'll discuss what options
|
them altogether isn't even theoretically possible. We'll discuss what options
|
||||||
yopu have if you hit a problem situation like this a little later.
|
you have if you hit a problem situation like this a little later.
|
||||||
|
|
||||||
## Python specific issues
|
## Python specific issues
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ operations:
|
||||||
that satisfy them. This is essentially where the finder interacts with the
|
that satisfy them. This is essentially where the finder interacts with the
|
||||||
resolver.
|
resolver.
|
||||||
* `is_satisfied_by` - checks if a candidate satisfies a requirement. This is
|
* `is_satisfied_by` - checks if a candidate satisfies a requirement. This is
|
||||||
basically the implementation of what a requirement meams.
|
basically the implementation of what a requirement means.
|
||||||
* `get_dependencies` - get the dependency metadata for a candidate. This is
|
* `get_dependencies` - get the dependency metadata for a candidate. This is
|
||||||
the implementation of the process of getting and reading package metadata.
|
the implementation of the process of getting and reading package metadata.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
sphinx ~= 6.0
|
sphinx ~= 7.0
|
||||||
towncrier
|
towncrier
|
||||||
furo
|
furo
|
||||||
myst_parser
|
myst_parser
|
||||||
|
|
1
news/11394.bugfix.rst
Normal file
1
news/11394.bugfix.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Ignore errors in temporary directory cleanup (show a warning instead).
|
1
news/12005.bugfix.rst
Normal file
1
news/12005.bugfix.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Removed uses of ``datetime.datetime.utcnow`` from non-vendored code.
|
1
news/12059.doc.rst
Normal file
1
news/12059.doc.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Document that ``pip search`` support has been removed from PyPI
|
6
news/12155.process.rst
Normal file
6
news/12155.process.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
The metadata-fetching log message is moved to the VERBOSE level and now hidden
|
||||||
|
by default. The more significant information in this message to most users are
|
||||||
|
already available in surrounding logs (the package name and version of the
|
||||||
|
metadata being fetched), while the URL to the exact metadata file is generally
|
||||||
|
too long and clutters the output. The message can be brought back with
|
||||||
|
``--verbose``.
|
1
news/12175.removal.rst
Normal file
1
news/12175.removal.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Drop a fallback to using SecureTransport on macOS. It was useful when pip detected OpenSSL older than 1.0.1, but the current pip does not support any Python version supporting such old OpenSSL versions.
|
1
news/12183.trivial.rst
Normal file
1
news/12183.trivial.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add test cases for some behaviors of ``install --dry-run`` and ``--use-feature=fast-deps``.
|
1
news/12187.bugfix.rst
Normal file
1
news/12187.bugfix.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fix improper handling of the new onexc argument of ``shutil.rmtree()`` in Python 3.12.
|
1
news/12191.bugfix.rst
Normal file
1
news/12191.bugfix.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Prevent downloading sdists twice when PEP 658 metadata is present.
|
1
news/12194.trivial.rst
Normal file
1
news/12194.trivial.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add lots of comments to the ``BuildTracker``.
|
1
news/12204.feature.rst
Normal file
1
news/12204.feature.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Improve use of datastructures to make candidate selection 1.6x faster
|
1
news/12215.feature.rst
Normal file
1
news/12215.feature.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Allow ``pip install --dry-run`` to use platform and ABI overriding options similar to ``--target``.
|
1
news/12224.feature.rst
Normal file
1
news/12224.feature.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add ``is_yanked`` boolean entry to the installation report (``--report``) to indicate whether the requirement was yanked from the index, but was still selected by pip conform to PEP 592.
|
1
news/12225.bugfix.rst
Normal file
1
news/12225.bugfix.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Filter out yanked links from the available versions error message: "(from versions: 1.0, 2.0, 3.0)" will not contain yanked versions conform PEP 592. The yanked versions (if any) will be mentioned in a separate error message.
|
0
news/12252.trivial.rst
Normal file
0
news/12252.trivial.rst
Normal file
1
news/12254.process.rst
Normal file
1
news/12254.process.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Added reference to `vulnerability reporting guidelines <https://www.python.org/dev/security/>`_ to pip's security policy.
|
0
news/12261.trivial.rst
Normal file
0
news/12261.trivial.rst
Normal file
1
news/4A0C40FF-ABE1-48C7-954C-7C3EB229135F.trivial.rst
Normal file
1
news/4A0C40FF-ABE1-48C7-954C-7C3EB229135F.trivial.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add ruff rules ASYNC,C4,C90,PERF,PLE,PLR for minor optimizations and to set upper limits on code complexity.
|
0
news/732404DE-8011-4146-8CAD-85D7756D88A6.trivial.rst
Normal file
0
news/732404DE-8011-4146-8CAD-85D7756D88A6.trivial.rst
Normal file
0
news/85F7E260-68FF-4C1E-A2CB-CF8634829D2D.trivial.rst
Normal file
0
news/85F7E260-68FF-4C1E-A2CB-CF8634829D2D.trivial.rst
Normal file
1
news/certifi.vendor.rst
Normal file
1
news/certifi.vendor.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Upgrade certifi to 2023.7.22
|
0
news/zhsdgdlsjgksdfj.trivial.rst
Normal file
0
news/zhsdgdlsjgksdfj.trivial.rst
Normal file
|
@ -67,7 +67,7 @@ def should_update_common_wheels() -> bool:
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Development Commands
|
# Development Commands
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "pypy3"])
|
@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"])
|
||||||
def test(session: nox.Session) -> None:
|
def test(session: nox.Session) -> None:
|
||||||
# Get the common wheels.
|
# Get the common wheels.
|
||||||
if should_update_common_wheels():
|
if should_update_common_wheels():
|
||||||
|
@ -89,6 +89,7 @@ def test(session: nox.Session) -> None:
|
||||||
shutil.rmtree(sdist_dir, ignore_errors=True)
|
shutil.rmtree(sdist_dir, ignore_errors=True)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
session.install("setuptools")
|
||||||
session.run(
|
session.run(
|
||||||
"python", "setup.py", "sdist", "--formats=zip", "--dist-dir", sdist_dir,
|
"python", "setup.py", "sdist", "--formats=zip", "--dist-dir", sdist_dir,
|
||||||
silent=True,
|
silent=True,
|
||||||
|
@ -219,7 +220,7 @@ def vendoring(session: nox.Session) -> None:
|
||||||
new_version = old_version
|
new_version = old_version
|
||||||
for inner_name, inner_version in pinned_requirements(vendor_txt):
|
for inner_name, inner_version in pinned_requirements(vendor_txt):
|
||||||
if inner_name == name:
|
if inner_name == name:
|
||||||
# this is a dedicated assignment, to make flake8 happy
|
# this is a dedicated assignment, to make lint happy
|
||||||
new_version = inner_version
|
new_version = inner_version
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -351,6 +352,7 @@ def build_dists(session: nox.Session) -> List[str]:
|
||||||
)
|
)
|
||||||
|
|
||||||
session.log("# Build distributions")
|
session.log("# Build distributions")
|
||||||
|
session.install("setuptools", "wheel")
|
||||||
session.run("python", "setup.py", "sdist", "bdist_wheel", silent=True)
|
session.run("python", "setup.py", "sdist", "bdist_wheel", silent=True)
|
||||||
produced_dists = glob.glob("dist/*")
|
produced_dists = glob.glob("dist/*")
|
||||||
|
|
||||||
|
|
|
@ -71,3 +71,55 @@ setuptools = "pkg_resources"
|
||||||
CacheControl = "https://raw.githubusercontent.com/ionrock/cachecontrol/v0.12.6/LICENSE.txt"
|
CacheControl = "https://raw.githubusercontent.com/ionrock/cachecontrol/v0.12.6/LICENSE.txt"
|
||||||
distlib = "https://bitbucket.org/pypa/distlib/raw/master/LICENSE.txt"
|
distlib = "https://bitbucket.org/pypa/distlib/raw/master/LICENSE.txt"
|
||||||
webencodings = "https://github.com/SimonSapin/python-webencodings/raw/master/LICENSE"
|
webencodings = "https://github.com/SimonSapin/python-webencodings/raw/master/LICENSE"
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
extend-exclude = [
|
||||||
|
"_vendor",
|
||||||
|
"./build",
|
||||||
|
".scratch",
|
||||||
|
"data",
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
"B019",
|
||||||
|
"B020",
|
||||||
|
"B904", # Ruff enables opinionated warnings by default
|
||||||
|
"B905", # Ruff enables opinionated warnings by default
|
||||||
|
"G202",
|
||||||
|
]
|
||||||
|
line-length = 88
|
||||||
|
select = [
|
||||||
|
"ASYNC",
|
||||||
|
"B",
|
||||||
|
"C4",
|
||||||
|
"C90",
|
||||||
|
"E",
|
||||||
|
"F",
|
||||||
|
"G",
|
||||||
|
"I",
|
||||||
|
"ISC",
|
||||||
|
"PERF",
|
||||||
|
"PLE",
|
||||||
|
"PLR0",
|
||||||
|
"W",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.isort]
|
||||||
|
# We need to explicitly make pip "first party" as it's imported by code in
|
||||||
|
# the docs and tests directories.
|
||||||
|
known-first-party = ["pip"]
|
||||||
|
known-third-party = ["pip._vendor"]
|
||||||
|
|
||||||
|
[tool.ruff.mccabe]
|
||||||
|
max-complexity = 33 # default is 10
|
||||||
|
|
||||||
|
[tool.ruff.per-file-ignores]
|
||||||
|
"noxfile.py" = ["G"]
|
||||||
|
"src/pip/_internal/*" = ["PERF203"]
|
||||||
|
"tests/*" = ["B011"]
|
||||||
|
"tests/unit/test_finder.py" = ["C414"]
|
||||||
|
|
||||||
|
[tool.ruff.pylint]
|
||||||
|
max-args = 15 # default is 5
|
||||||
|
max-branches = 28 # default is 12
|
||||||
|
max-returns = 13 # default is 6
|
||||||
|
max-statements = 134 # default is 50
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -42,6 +42,7 @@ setup(
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
__version__ = "23.2.dev0"
|
__version__ = "23.3.dev0"
|
||||||
|
|
||||||
|
|
||||||
def main(args: Optional[List[str]] = None) -> int:
|
def main(args: Optional[List[str]] = None) -> int:
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
|
|
||||||
# Remove '' and current working directory from the first entry
|
# Remove '' and current working directory from the first entry
|
||||||
# of sys.path, if present to avoid using current directory
|
# of sys.path, if present to avoid using current directory
|
||||||
|
@ -20,12 +19,6 @@ if __package__ == "":
|
||||||
sys.path.insert(0, path)
|
sys.path.insert(0, path)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Work around the error reported in #9540, pending a proper fix.
|
|
||||||
# Note: It is essential the warning filter is set *before* importing
|
|
||||||
# pip, as the deprecation happens at import time, not runtime.
|
|
||||||
warnings.filterwarnings(
|
|
||||||
"ignore", category=DeprecationWarning, module=".*packaging\\.version"
|
|
||||||
)
|
|
||||||
from pip._internal.cli.main import main as _main
|
from pip._internal.cli.main import main as _main
|
||||||
|
|
||||||
sys.exit(_main())
|
sys.exit(_main())
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import pip._internal.utils.inject_securetransport # noqa
|
|
||||||
from pip._internal.utils import _log
|
from pip._internal.utils import _log
|
||||||
|
|
||||||
# init_logging() must be called before any call to logging.getLogger()
|
# init_logging() must be called before any call to logging.getLogger()
|
||||||
|
|
|
@ -78,12 +78,10 @@ class Cache:
|
||||||
if can_not_cache:
|
if can_not_cache:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
candidates = []
|
|
||||||
path = self.get_path_for_link(link)
|
path = self.get_path_for_link(link)
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
for candidate in os.listdir(path):
|
return [(candidate, path) for candidate in os.listdir(path)]
|
||||||
candidates.append((candidate, path))
|
return []
|
||||||
return candidates
|
|
||||||
|
|
||||||
def get_path_for_link(self, link: Link) -> str:
|
def get_path_for_link(self, link: Link) -> str:
|
||||||
"""Return a directory to store cached items in for link."""
|
"""Return a directory to store cached items in for link."""
|
||||||
|
@ -194,7 +192,17 @@ class CacheEntry:
|
||||||
self.origin: Optional[DirectUrl] = None
|
self.origin: Optional[DirectUrl] = None
|
||||||
origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
|
origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
|
||||||
if origin_direct_url_path.exists():
|
if origin_direct_url_path.exists():
|
||||||
self.origin = DirectUrl.from_json(origin_direct_url_path.read_text())
|
try:
|
||||||
|
self.origin = DirectUrl.from_json(
|
||||||
|
origin_direct_url_path.read_text(encoding="utf-8")
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
"Ignoring invalid cache entry origin file %s for %s (%s)",
|
||||||
|
origin_direct_url_path,
|
||||||
|
link.filename,
|
||||||
|
e,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WheelCache(Cache):
|
class WheelCache(Cache):
|
||||||
|
@ -257,16 +265,26 @@ class WheelCache(Cache):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
|
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
|
||||||
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
|
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
|
||||||
if origin_path.is_file():
|
if origin_path.exists():
|
||||||
origin = DirectUrl.from_json(origin_path.read_text())
|
try:
|
||||||
# TODO: use DirectUrl.equivalent when https://github.com/pypa/pip/pull/10564
|
origin = DirectUrl.from_json(origin_path.read_text(encoding="utf-8"))
|
||||||
# is merged.
|
except Exception as e:
|
||||||
if origin.url != download_info.url:
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Origin URL %s in cache entry %s does not match download URL %s. "
|
"Could not read origin file %s in cache entry (%s). "
|
||||||
"This is likely a pip bug or a cache corruption issue.",
|
"Will attempt to overwrite it.",
|
||||||
origin.url,
|
origin_path,
|
||||||
cache_dir,
|
e,
|
||||||
download_info.url,
|
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# TODO: use DirectUrl.equivalent when
|
||||||
|
# https://github.com/pypa/pip/pull/10564 is merged.
|
||||||
|
if origin.url != download_info.url:
|
||||||
|
logger.warning(
|
||||||
|
"Origin URL %s in cache entry %s does not match download URL "
|
||||||
|
"%s. This is likely a pip bug or a cache corruption issue. "
|
||||||
|
"Will overwrite it with the new value.",
|
||||||
|
origin.url,
|
||||||
|
cache_dir,
|
||||||
|
download_info.url,
|
||||||
|
)
|
||||||
origin_path.write_text(download_info.to_json(), encoding="utf-8")
|
origin_path.write_text(download_info.to_json(), encoding="utf-8")
|
||||||
|
|
|
@ -71,8 +71,9 @@ def autocomplete() -> None:
|
||||||
|
|
||||||
for opt in subcommand.parser.option_list_all:
|
for opt in subcommand.parser.option_list_all:
|
||||||
if opt.help != optparse.SUPPRESS_HELP:
|
if opt.help != optparse.SUPPRESS_HELP:
|
||||||
for opt_str in opt._long_opts + opt._short_opts:
|
options += [
|
||||||
options.append((opt_str, opt.nargs))
|
(opt_str, opt.nargs) for opt_str in opt._long_opts + opt._short_opts
|
||||||
|
]
|
||||||
|
|
||||||
# filter out previously specified options from available options
|
# filter out previously specified options from available options
|
||||||
prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]]
|
prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]]
|
||||||
|
|
|
@ -131,6 +131,17 @@ class Command(CommandContextMixIn):
|
||||||
", ".join(sorted(always_enabled_features)),
|
", ".join(sorted(always_enabled_features)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Make sure that the --python argument isn't specified after the
|
||||||
|
# subcommand. We can tell, because if --python was specified,
|
||||||
|
# we should only reach this point if we're running in the created
|
||||||
|
# subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment
|
||||||
|
# variable set.
|
||||||
|
if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
|
||||||
|
logger.critical(
|
||||||
|
"The --python option must be placed before the pip subcommand name"
|
||||||
|
)
|
||||||
|
sys.exit(ERROR)
|
||||||
|
|
||||||
# TODO: Try to get these passing down from the command?
|
# TODO: Try to get these passing down from the command?
|
||||||
# without resorting to os.environ to hold these.
|
# without resorting to os.environ to hold these.
|
||||||
# This also affects isolated builds and it should.
|
# This also affects isolated builds and it should.
|
||||||
|
|
|
@ -92,10 +92,10 @@ def check_dist_restriction(options: Values, check_target: bool = False) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
if check_target:
|
if check_target:
|
||||||
if dist_restriction_set and not options.target_dir:
|
if not options.dry_run and dist_restriction_set and not options.target_dir:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
"Can not use any platform or abi specific options unless "
|
"Can not use any platform or abi specific options unless "
|
||||||
"installing via '--target'"
|
"installing via '--target' or using '--dry-run'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -287,6 +287,7 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
"""
|
"""
|
||||||
temp_build_dir_path = temp_build_dir.path
|
temp_build_dir_path = temp_build_dir.path
|
||||||
assert temp_build_dir_path is not None
|
assert temp_build_dir_path is not None
|
||||||
|
legacy_resolver = False
|
||||||
|
|
||||||
resolver_variant = cls.determine_resolver_variant(options)
|
resolver_variant = cls.determine_resolver_variant(options)
|
||||||
if resolver_variant == "2020-resolver":
|
if resolver_variant == "2020-resolver":
|
||||||
|
@ -300,6 +301,7 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
"production."
|
"production."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
legacy_resolver = True
|
||||||
lazy_wheel = False
|
lazy_wheel = False
|
||||||
if "fast-deps" in options.features_enabled:
|
if "fast-deps" in options.features_enabled:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
@ -320,6 +322,7 @@ class RequirementCommand(IndexGroupCommand):
|
||||||
use_user_site=use_user_site,
|
use_user_site=use_user_site,
|
||||||
lazy_wheel=lazy_wheel,
|
lazy_wheel=lazy_wheel,
|
||||||
verbosity=verbosity,
|
verbosity=verbosity,
|
||||||
|
legacy_resolver=legacy_resolver,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -3,10 +3,10 @@ import textwrap
|
||||||
from optparse import Values
|
from optparse import Values
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
import pip._internal.utils.filesystem as filesystem
|
|
||||||
from pip._internal.cli.base_command import Command
|
from pip._internal.cli.base_command import Command
|
||||||
from pip._internal.cli.status_codes import ERROR, SUCCESS
|
from pip._internal.cli.status_codes import ERROR, SUCCESS
|
||||||
from pip._internal.exceptions import CommandError, PipError
|
from pip._internal.exceptions import CommandError, PipError
|
||||||
|
from pip._internal.utils import filesystem
|
||||||
from pip._internal.utils.logging import getLogger
|
from pip._internal.utils.logging import getLogger
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
@ -151,14 +151,8 @@ class CacheCommand(Command):
|
||||||
logger.info("\n".join(sorted(results)))
|
logger.info("\n".join(sorted(results)))
|
||||||
|
|
||||||
def format_for_abspath(self, files: List[str]) -> None:
|
def format_for_abspath(self, files: List[str]) -> None:
|
||||||
if not files:
|
if files:
|
||||||
return
|
logger.info("\n".join(sorted(files)))
|
||||||
|
|
||||||
results = []
|
|
||||||
for filename in files:
|
|
||||||
results.append(filename)
|
|
||||||
|
|
||||||
logger.info("\n".join(sorted(results)))
|
|
||||||
|
|
||||||
def remove_cache_items(self, options: Values, args: List[Any]) -> None:
|
def remove_cache_items(self, options: Values, args: List[Any]) -> None:
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
|
|
|
@ -7,6 +7,7 @@ from pip._internal.cli.status_codes import ERROR, SUCCESS
|
||||||
from pip._internal.operations.check import (
|
from pip._internal.operations.check import (
|
||||||
check_package_set,
|
check_package_set,
|
||||||
create_package_set_from_installed,
|
create_package_set_from_installed,
|
||||||
|
warn_legacy_versions_and_specifiers,
|
||||||
)
|
)
|
||||||
from pip._internal.utils.misc import write_output
|
from pip._internal.utils.misc import write_output
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ class CheckCommand(Command):
|
||||||
|
|
||||||
def run(self, options: Values, args: List[str]) -> int:
|
def run(self, options: Values, args: List[str]) -> int:
|
||||||
package_set, parsing_probs = create_package_set_from_installed()
|
package_set, parsing_probs = create_package_set_from_installed()
|
||||||
|
warn_legacy_versions_and_specifiers(package_set)
|
||||||
missing, conflicting = check_package_set(package_set)
|
missing, conflicting = check_package_set(package_set)
|
||||||
|
|
||||||
for project_name in missing:
|
for project_name in missing:
|
||||||
|
|
|
@ -22,15 +22,10 @@ COMPLETION_SCRIPTS = {
|
||||||
complete -o default -F _pip_completion {prog}
|
complete -o default -F _pip_completion {prog}
|
||||||
""",
|
""",
|
||||||
"zsh": """
|
"zsh": """
|
||||||
function _pip_completion {{
|
#compdef -P pip[0-9.]#
|
||||||
local words cword
|
compadd $( COMP_WORDS="$words[*]" \\
|
||||||
read -Ac words
|
COMP_CWORD=$((CURRENT-1)) \\
|
||||||
read -cn cword
|
PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )
|
||||||
reply=( $( COMP_WORDS="$words[*]" \\
|
|
||||||
COMP_CWORD=$(( cword-1 )) \\
|
|
||||||
PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null ))
|
|
||||||
}}
|
|
||||||
compctl -K _pip_completion {prog}
|
|
||||||
""",
|
""",
|
||||||
"fish": """
|
"fish": """
|
||||||
function __fish_complete_pip
|
function __fish_complete_pip
|
||||||
|
|
|
@ -105,7 +105,7 @@ def show_tags(options: Values) -> None:
|
||||||
tag_limit = 10
|
tag_limit = 10
|
||||||
|
|
||||||
target_python = make_target_python(options)
|
target_python = make_target_python(options)
|
||||||
tags = target_python.get_tags()
|
tags = target_python.get_sorted_tags()
|
||||||
|
|
||||||
# Display the target options that were explicitly provided.
|
# Display the target options that were explicitly provided.
|
||||||
formatted_target = target_python.format_given()
|
formatted_target = target_python.format_given()
|
||||||
|
@ -134,10 +134,9 @@ def show_tags(options: Values) -> None:
|
||||||
|
|
||||||
|
|
||||||
def ca_bundle_info(config: Configuration) -> str:
|
def ca_bundle_info(config: Configuration) -> str:
|
||||||
levels = set()
|
# Ruff misidentifies config as a dict.
|
||||||
for key, _ in config.items():
|
# Configuration does not have support the mapping interface.
|
||||||
levels.add(key.split(".")[0])
|
levels = {key.split(".", 1)[0] for key, _ in config.items()} # noqa: PERF102
|
||||||
|
|
||||||
if not levels:
|
if not levels:
|
||||||
return "Not specified"
|
return "Not specified"
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,10 @@ class DownloadCommand(RequirementCommand):
|
||||||
assert req.name is not None
|
assert req.name is not None
|
||||||
preparer.save_linked_requirement(req)
|
preparer.save_linked_requirement(req)
|
||||||
downloaded.append(req.name)
|
downloaded.append(req.name)
|
||||||
|
|
||||||
|
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
|
||||||
|
requirement_set.warn_legacy_versions_and_specifiers()
|
||||||
|
|
||||||
if downloaded:
|
if downloaded:
|
||||||
write_output("Successfully downloaded %s", " ".join(downloaded))
|
write_output("Successfully downloaded %s", " ".join(downloaded))
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import sys
|
import sys
|
||||||
from optparse import Values
|
from optparse import Values
|
||||||
from typing import List
|
from typing import AbstractSet, List
|
||||||
|
|
||||||
from pip._internal.cli import cmdoptions
|
from pip._internal.cli import cmdoptions
|
||||||
from pip._internal.cli.base_command import Command
|
from pip._internal.cli.base_command import Command
|
||||||
|
@ -8,7 +8,18 @@ from pip._internal.cli.status_codes import SUCCESS
|
||||||
from pip._internal.operations.freeze import freeze
|
from pip._internal.operations.freeze import freeze
|
||||||
from pip._internal.utils.compat import stdlib_pkgs
|
from pip._internal.utils.compat import stdlib_pkgs
|
||||||
|
|
||||||
DEV_PKGS = {"pip", "setuptools", "distribute", "wheel"}
|
|
||||||
|
def _should_suppress_build_backends() -> bool:
|
||||||
|
return sys.version_info < (3, 12)
|
||||||
|
|
||||||
|
|
||||||
|
def _dev_pkgs() -> AbstractSet[str]:
|
||||||
|
pkgs = {"pip"}
|
||||||
|
|
||||||
|
if _should_suppress_build_backends():
|
||||||
|
pkgs |= {"setuptools", "distribute", "wheel"}
|
||||||
|
|
||||||
|
return pkgs
|
||||||
|
|
||||||
|
|
||||||
class FreezeCommand(Command):
|
class FreezeCommand(Command):
|
||||||
|
@ -61,7 +72,7 @@ class FreezeCommand(Command):
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help=(
|
help=(
|
||||||
"Do not skip these packages in the output:"
|
"Do not skip these packages in the output:"
|
||||||
" {}".format(", ".join(DEV_PKGS))
|
" {}".format(", ".join(_dev_pkgs()))
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.cmd_opts.add_option(
|
self.cmd_opts.add_option(
|
||||||
|
@ -77,7 +88,7 @@ class FreezeCommand(Command):
|
||||||
def run(self, options: Values, args: List[str]) -> int:
|
def run(self, options: Values, args: List[str]) -> int:
|
||||||
skip = set(stdlib_pkgs)
|
skip = set(stdlib_pkgs)
|
||||||
if not options.freeze_all:
|
if not options.freeze_all:
|
||||||
skip.update(DEV_PKGS)
|
skip.update(_dev_pkgs())
|
||||||
|
|
||||||
if options.excludes:
|
if options.excludes:
|
||||||
skip.update(options.excludes)
|
skip.update(options.excludes)
|
||||||
|
|
|
@ -387,6 +387,9 @@ class InstallCommand(RequirementCommand):
|
||||||
json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
|
json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
if options.dry_run:
|
if options.dry_run:
|
||||||
|
# In non dry-run mode, the legacy versions and specifiers check
|
||||||
|
# will be done as part of conflict detection.
|
||||||
|
requirement_set.warn_legacy_versions_and_specifiers()
|
||||||
would_install_items = sorted(
|
would_install_items = sorted(
|
||||||
(r.metadata["name"], r.metadata["version"])
|
(r.metadata["name"], r.metadata["version"])
|
||||||
for r in requirement_set.requirements_to_install
|
for r in requirement_set.requirements_to_install
|
||||||
|
|
|
@ -103,7 +103,10 @@ class ListCommand(IndexGroupCommand):
|
||||||
dest="list_format",
|
dest="list_format",
|
||||||
default="columns",
|
default="columns",
|
||||||
choices=("columns", "freeze", "json"),
|
choices=("columns", "freeze", "json"),
|
||||||
help="Select the output format among: columns (default), freeze, or json",
|
help=(
|
||||||
|
"Select the output format among: columns (default), freeze, or json. "
|
||||||
|
"The 'freeze' format cannot be used with the --outdated option."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.cmd_opts.add_option(
|
self.cmd_opts.add_option(
|
||||||
|
@ -157,7 +160,7 @@ class ListCommand(IndexGroupCommand):
|
||||||
|
|
||||||
if options.outdated and options.list_format == "freeze":
|
if options.outdated and options.list_format == "freeze":
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
"List format 'freeze' can not be used with the --outdated option."
|
"List format 'freeze' cannot be used with the --outdated option."
|
||||||
)
|
)
|
||||||
|
|
||||||
cmdoptions.check_list_path_option(options)
|
cmdoptions.check_list_path_option(options)
|
||||||
|
@ -294,7 +297,7 @@ class ListCommand(IndexGroupCommand):
|
||||||
|
|
||||||
# Create and add a separator.
|
# Create and add a separator.
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
pkg_strings.insert(1, " ".join(map(lambda x: "-" * x, sizes)))
|
pkg_strings.insert(1, " ".join("-" * x for x in sizes))
|
||||||
|
|
||||||
for val in pkg_strings:
|
for val in pkg_strings:
|
||||||
write_output(val)
|
write_output(val)
|
||||||
|
|
|
@ -153,6 +153,9 @@ class WheelCommand(RequirementCommand):
|
||||||
elif should_build_for_wheel_command(req):
|
elif should_build_for_wheel_command(req):
|
||||||
reqs_to_build.append(req)
|
reqs_to_build.append(req)
|
||||||
|
|
||||||
|
preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
|
||||||
|
requirement_set.warn_legacy_versions_and_specifiers()
|
||||||
|
|
||||||
# build wheels
|
# build wheels
|
||||||
build_successes, build_failures = build(
|
build_successes, build_failures = build(
|
||||||
reqs_to_build,
|
reqs_to_build,
|
||||||
|
|
|
@ -210,8 +210,15 @@ class Configuration:
|
||||||
# Ensure directory exists.
|
# Ensure directory exists.
|
||||||
ensure_dir(os.path.dirname(fname))
|
ensure_dir(os.path.dirname(fname))
|
||||||
|
|
||||||
with open(fname, "w") as f:
|
# Ensure directory's permission(need to be writeable)
|
||||||
parser.write(f)
|
try:
|
||||||
|
with open(fname, "w") as f:
|
||||||
|
parser.write(f)
|
||||||
|
except OSError as error:
|
||||||
|
raise ConfigurationError(
|
||||||
|
f"An error occurred while writing to the configuration file "
|
||||||
|
f"{fname}: {error}"
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Private routines
|
# Private routines
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import abc
|
import abc
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pip._internal.index.package_finder import PackageFinder
|
from pip._internal.index.package_finder import PackageFinder
|
||||||
from pip._internal.metadata.base import BaseDistribution
|
from pip._internal.metadata.base import BaseDistribution
|
||||||
|
@ -19,12 +20,23 @@ class AbstractDistribution(metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
- we must be able to create a Distribution object exposing the
|
- we must be able to create a Distribution object exposing the
|
||||||
above metadata.
|
above metadata.
|
||||||
|
|
||||||
|
- if we need to do work in the build tracker, we must be able to generate a unique
|
||||||
|
string to identify the requirement in the build tracker.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, req: InstallRequirement) -> None:
|
def __init__(self, req: InstallRequirement) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.req = req
|
self.req = req
|
||||||
|
|
||||||
|
@abc.abstractproperty
|
||||||
|
def build_tracker_id(self) -> Optional[str]:
|
||||||
|
"""A string that uniquely identifies this requirement to the build tracker.
|
||||||
|
|
||||||
|
If None, then this dist has no work to do in the build tracker, and
|
||||||
|
``.prepare_distribution_metadata()`` will not be called."""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_metadata_distribution(self) -> BaseDistribution:
|
def get_metadata_distribution(self) -> BaseDistribution:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pip._internal.distributions.base import AbstractDistribution
|
from pip._internal.distributions.base import AbstractDistribution
|
||||||
from pip._internal.index.package_finder import PackageFinder
|
from pip._internal.index.package_finder import PackageFinder
|
||||||
from pip._internal.metadata import BaseDistribution
|
from pip._internal.metadata import BaseDistribution
|
||||||
|
@ -10,6 +12,10 @@ class InstalledDistribution(AbstractDistribution):
|
||||||
been computed.
|
been computed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def build_tracker_id(self) -> Optional[str]:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_metadata_distribution(self) -> BaseDistribution:
|
def get_metadata_distribution(self) -> BaseDistribution:
|
||||||
assert self.req.satisfied_by is not None, "not actually installed"
|
assert self.req.satisfied_by is not None, "not actually installed"
|
||||||
return self.req.satisfied_by
|
return self.req.satisfied_by
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Iterable, Set, Tuple
|
from typing import Iterable, Optional, Set, Tuple
|
||||||
|
|
||||||
from pip._internal.build_env import BuildEnvironment
|
from pip._internal.build_env import BuildEnvironment
|
||||||
from pip._internal.distributions.base import AbstractDistribution
|
from pip._internal.distributions.base import AbstractDistribution
|
||||||
|
@ -18,6 +18,12 @@ class SourceDistribution(AbstractDistribution):
|
||||||
generated, either using PEP 517 or using the legacy `setup.py egg_info`.
|
generated, either using PEP 517 or using the legacy `setup.py egg_info`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def build_tracker_id(self) -> Optional[str]:
|
||||||
|
"""Identify this requirement uniquely by its link."""
|
||||||
|
assert self.req.link
|
||||||
|
return self.req.link.url_without_fragment
|
||||||
|
|
||||||
def get_metadata_distribution(self) -> BaseDistribution:
|
def get_metadata_distribution(self) -> BaseDistribution:
|
||||||
return self.req.get_dist()
|
return self.req.get_dist()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
|
|
||||||
from pip._internal.distributions.base import AbstractDistribution
|
from pip._internal.distributions.base import AbstractDistribution
|
||||||
|
@ -15,6 +17,10 @@ class WheelDistribution(AbstractDistribution):
|
||||||
This does not need any preparation as wheels can be directly unpacked.
|
This does not need any preparation as wheels can be directly unpacked.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def build_tracker_id(self) -> Optional[str]:
|
||||||
|
return None
|
||||||
|
|
||||||
def get_metadata_distribution(self) -> BaseDistribution:
|
def get_metadata_distribution(self) -> BaseDistribution:
|
||||||
"""Loads the metadata from the wheel file into memory and returns a
|
"""Loads the metadata from the wheel file into memory and returns a
|
||||||
Distribution that uses it, not relying on the wheel file or
|
Distribution that uses it, not relying on the wheel file or
|
||||||
|
|
|
@ -544,7 +544,7 @@ class HashMissing(HashError):
|
||||||
# so the output can be directly copied into the requirements file.
|
# so the output can be directly copied into the requirements file.
|
||||||
package = (
|
package = (
|
||||||
self.req.original_link
|
self.req.original_link
|
||||||
if self.req.original_link
|
if self.req.is_direct
|
||||||
# In case someone feeds something downright stupid
|
# In case someone feeds something downright stupid
|
||||||
# to InstallRequirement's constructor.
|
# to InstallRequirement's constructor.
|
||||||
else getattr(self.req, "req", None)
|
else getattr(self.req, "req", None)
|
||||||
|
|
|
@ -198,7 +198,7 @@ class LinkEvaluator:
|
||||||
reason = f"wrong project name (not {self.project_name})"
|
reason = f"wrong project name (not {self.project_name})"
|
||||||
return (LinkType.different_project, reason)
|
return (LinkType.different_project, reason)
|
||||||
|
|
||||||
supported_tags = self._target_python.get_tags()
|
supported_tags = self._target_python.get_unsorted_tags()
|
||||||
if not wheel.supported(supported_tags):
|
if not wheel.supported(supported_tags):
|
||||||
# Include the wheel's tags in the reason string to
|
# Include the wheel's tags in the reason string to
|
||||||
# simplify troubleshooting compatibility issues.
|
# simplify troubleshooting compatibility issues.
|
||||||
|
@ -414,7 +414,7 @@ class CandidateEvaluator:
|
||||||
if specifier is None:
|
if specifier is None:
|
||||||
specifier = specifiers.SpecifierSet()
|
specifier = specifiers.SpecifierSet()
|
||||||
|
|
||||||
supported_tags = target_python.get_tags()
|
supported_tags = target_python.get_sorted_tags()
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
project_name=project_name,
|
project_name=project_name,
|
||||||
|
|
|
@ -89,7 +89,7 @@ def distutils_scheme(
|
||||||
# finalize_options(); we only want to override here if the user
|
# finalize_options(); we only want to override here if the user
|
||||||
# has explicitly requested it hence going back to the config
|
# has explicitly requested it hence going back to the config
|
||||||
if "install_lib" in d.get_option_dict("install"):
|
if "install_lib" in d.get_option_dict("install"):
|
||||||
scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib))
|
scheme.update({"purelib": i.install_lib, "platlib": i.install_lib})
|
||||||
|
|
||||||
if running_under_virtualenv():
|
if running_under_virtualenv():
|
||||||
if home:
|
if home:
|
||||||
|
|
|
@ -151,7 +151,7 @@ def _emit_egg_deprecation(location: Optional[str]) -> None:
|
||||||
deprecated(
|
deprecated(
|
||||||
reason=f"Loading egg at {location} is deprecated.",
|
reason=f"Loading egg at {location} is deprecated.",
|
||||||
replacement="to use pip for package installation.",
|
replacement="to use pip for package installation.",
|
||||||
gone_in=None,
|
gone_in="23.3",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ class Environment(BaseEnvironment):
|
||||||
for location in self._paths:
|
for location in self._paths:
|
||||||
yield from finder.find(location)
|
yield from finder.find(location)
|
||||||
for dist in finder.find_eggs(location):
|
for dist in finder.find_eggs(location):
|
||||||
# _emit_egg_deprecation(dist.location) # TODO: Enable this.
|
_emit_egg_deprecation(dist.location)
|
||||||
yield dist
|
yield dist
|
||||||
# This must go last because that's how pkg_resources tie-breaks.
|
# This must go last because that's how pkg_resources tie-breaks.
|
||||||
yield from finder.find_linked(location)
|
yield from finder.find_linked(location)
|
||||||
|
|
|
@ -22,7 +22,10 @@ class InstallationReport:
|
||||||
# is_direct is true if the requirement was a direct URL reference (which
|
# is_direct is true if the requirement was a direct URL reference (which
|
||||||
# includes editable requirements), and false if the requirement was
|
# includes editable requirements), and false if the requirement was
|
||||||
# downloaded from a PEP 503 index or --find-links.
|
# downloaded from a PEP 503 index or --find-links.
|
||||||
"is_direct": bool(ireq.original_link),
|
"is_direct": ireq.is_direct,
|
||||||
|
# is_yanked is true if the requirement was yanked from the index, but
|
||||||
|
# was still selected by pip to conform to PEP 592.
|
||||||
|
"is_yanked": ireq.link.is_yanked if ireq.link else False,
|
||||||
# requested is true if the requirement was specified by the user (aka
|
# requested is true if the requirement was specified by the user (aka
|
||||||
# top level requirement), and false if it was installed as a dependency of a
|
# top level requirement), and false if it was installed as a dependency of a
|
||||||
# requirement. https://peps.python.org/pep-0376/#requested
|
# requirement. https://peps.python.org/pep-0376/#requested
|
||||||
|
@ -33,7 +36,7 @@ class InstallationReport:
|
||||||
}
|
}
|
||||||
if ireq.user_supplied and ireq.extras:
|
if ireq.user_supplied and ireq.extras:
|
||||||
# For top level requirements, the list of requested extras, if any.
|
# For top level requirements, the list of requested extras, if any.
|
||||||
res["requested_extras"] = list(sorted(ireq.extras))
|
res["requested_extras"] = sorted(ireq.extras)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
|
|
|
@ -69,18 +69,6 @@ class LinkHash:
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
assert self.name in _SUPPORTED_HASHES
|
assert self.name in _SUPPORTED_HASHES
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def parse_pep658_hash(cls, dist_info_metadata: str) -> Optional["LinkHash"]:
|
|
||||||
"""Parse a PEP 658 data-dist-info-metadata hash."""
|
|
||||||
if dist_info_metadata == "true":
|
|
||||||
return None
|
|
||||||
name, sep, value = dist_info_metadata.partition("=")
|
|
||||||
if not sep:
|
|
||||||
return None
|
|
||||||
if name not in _SUPPORTED_HASHES:
|
|
||||||
return None
|
|
||||||
return cls(name=name, value=value)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@functools.lru_cache(maxsize=None)
|
@functools.lru_cache(maxsize=None)
|
||||||
def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]:
|
def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]:
|
||||||
|
@ -107,6 +95,28 @@ class LinkHash:
|
||||||
return hashes.is_hash_allowed(self.name, hex_digest=self.value)
|
return hashes.is_hash_allowed(self.name, hex_digest=self.value)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class MetadataFile:
|
||||||
|
"""Information about a core metadata file associated with a distribution."""
|
||||||
|
|
||||||
|
hashes: Optional[Dict[str, str]]
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
if self.hashes is not None:
|
||||||
|
assert all(name in _SUPPORTED_HASHES for name in self.hashes)
|
||||||
|
|
||||||
|
|
||||||
|
def supported_hashes(hashes: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]:
|
||||||
|
# Remove any unsupported hash types from the mapping. If this leaves no
|
||||||
|
# supported hashes, return None
|
||||||
|
if hashes is None:
|
||||||
|
return None
|
||||||
|
hashes = {n: v for n, v in hashes.items() if n in _SUPPORTED_HASHES}
|
||||||
|
if not hashes:
|
||||||
|
return None
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
|
||||||
def _clean_url_path_part(part: str) -> str:
|
def _clean_url_path_part(part: str) -> str:
|
||||||
"""
|
"""
|
||||||
Clean a "part" of a URL path (i.e. after splitting on "@" characters).
|
Clean a "part" of a URL path (i.e. after splitting on "@" characters).
|
||||||
|
@ -179,7 +189,7 @@ class Link(KeyBasedCompareMixin):
|
||||||
"comes_from",
|
"comes_from",
|
||||||
"requires_python",
|
"requires_python",
|
||||||
"yanked_reason",
|
"yanked_reason",
|
||||||
"dist_info_metadata",
|
"metadata_file_data",
|
||||||
"cache_link_parsing",
|
"cache_link_parsing",
|
||||||
"egg_fragment",
|
"egg_fragment",
|
||||||
]
|
]
|
||||||
|
@ -190,7 +200,7 @@ class Link(KeyBasedCompareMixin):
|
||||||
comes_from: Optional[Union[str, "IndexContent"]] = None,
|
comes_from: Optional[Union[str, "IndexContent"]] = None,
|
||||||
requires_python: Optional[str] = None,
|
requires_python: Optional[str] = None,
|
||||||
yanked_reason: Optional[str] = None,
|
yanked_reason: Optional[str] = None,
|
||||||
dist_info_metadata: Optional[str] = None,
|
metadata_file_data: Optional[MetadataFile] = None,
|
||||||
cache_link_parsing: bool = True,
|
cache_link_parsing: bool = True,
|
||||||
hashes: Optional[Mapping[str, str]] = None,
|
hashes: Optional[Mapping[str, str]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -208,11 +218,10 @@ class Link(KeyBasedCompareMixin):
|
||||||
a simple repository HTML link. If the file has been yanked but
|
a simple repository HTML link. If the file has been yanked but
|
||||||
no reason was provided, this should be the empty string. See
|
no reason was provided, this should be the empty string. See
|
||||||
PEP 592 for more information and the specification.
|
PEP 592 for more information and the specification.
|
||||||
:param dist_info_metadata: the metadata attached to the file, or None if no such
|
:param metadata_file_data: the metadata attached to the file, or None if
|
||||||
metadata is provided. This is the value of the "data-dist-info-metadata"
|
no such metadata is provided. This argument, if not None, indicates
|
||||||
attribute, if present, in a simple repository HTML link. This may be parsed
|
that a separate metadata file exists, and also optionally supplies
|
||||||
into its own `Link` by `self.metadata_link()`. See PEP 658 for more
|
hashes for that file.
|
||||||
information and the specification.
|
|
||||||
:param cache_link_parsing: A flag that is used elsewhere to determine
|
:param cache_link_parsing: A flag that is used elsewhere to determine
|
||||||
whether resources retrieved from this link should be cached. PyPI
|
whether resources retrieved from this link should be cached. PyPI
|
||||||
URLs should generally have this set to False, for example.
|
URLs should generally have this set to False, for example.
|
||||||
|
@ -220,6 +229,10 @@ class Link(KeyBasedCompareMixin):
|
||||||
determine the validity of a download.
|
determine the validity of a download.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# The comes_from, requires_python, and metadata_file_data arguments are
|
||||||
|
# only used by classmethods of this class, and are not used in client
|
||||||
|
# code directly.
|
||||||
|
|
||||||
# url can be a UNC windows share
|
# url can be a UNC windows share
|
||||||
if url.startswith("\\\\"):
|
if url.startswith("\\\\"):
|
||||||
url = path_to_url(url)
|
url = path_to_url(url)
|
||||||
|
@ -239,7 +252,7 @@ class Link(KeyBasedCompareMixin):
|
||||||
self.comes_from = comes_from
|
self.comes_from = comes_from
|
||||||
self.requires_python = requires_python if requires_python else None
|
self.requires_python = requires_python if requires_python else None
|
||||||
self.yanked_reason = yanked_reason
|
self.yanked_reason = yanked_reason
|
||||||
self.dist_info_metadata = dist_info_metadata
|
self.metadata_file_data = metadata_file_data
|
||||||
|
|
||||||
super().__init__(key=url, defining_class=Link)
|
super().__init__(key=url, defining_class=Link)
|
||||||
|
|
||||||
|
@ -262,9 +275,25 @@ class Link(KeyBasedCompareMixin):
|
||||||
url = _ensure_quoted_url(urllib.parse.urljoin(page_url, file_url))
|
url = _ensure_quoted_url(urllib.parse.urljoin(page_url, file_url))
|
||||||
pyrequire = file_data.get("requires-python")
|
pyrequire = file_data.get("requires-python")
|
||||||
yanked_reason = file_data.get("yanked")
|
yanked_reason = file_data.get("yanked")
|
||||||
dist_info_metadata = file_data.get("dist-info-metadata")
|
|
||||||
hashes = file_data.get("hashes", {})
|
hashes = file_data.get("hashes", {})
|
||||||
|
|
||||||
|
# PEP 714: Indexes must use the name core-metadata, but
|
||||||
|
# clients should support the old name as a fallback for compatibility.
|
||||||
|
metadata_info = file_data.get("core-metadata")
|
||||||
|
if metadata_info is None:
|
||||||
|
metadata_info = file_data.get("dist-info-metadata")
|
||||||
|
|
||||||
|
# The metadata info value may be a boolean, or a dict of hashes.
|
||||||
|
if isinstance(metadata_info, dict):
|
||||||
|
# The file exists, and hashes have been supplied
|
||||||
|
metadata_file_data = MetadataFile(supported_hashes(metadata_info))
|
||||||
|
elif metadata_info:
|
||||||
|
# The file exists, but there are no hashes
|
||||||
|
metadata_file_data = MetadataFile(None)
|
||||||
|
else:
|
||||||
|
# False or not present: the file does not exist
|
||||||
|
metadata_file_data = None
|
||||||
|
|
||||||
# The Link.yanked_reason expects an empty string instead of a boolean.
|
# The Link.yanked_reason expects an empty string instead of a boolean.
|
||||||
if yanked_reason and not isinstance(yanked_reason, str):
|
if yanked_reason and not isinstance(yanked_reason, str):
|
||||||
yanked_reason = ""
|
yanked_reason = ""
|
||||||
|
@ -278,7 +307,7 @@ class Link(KeyBasedCompareMixin):
|
||||||
requires_python=pyrequire,
|
requires_python=pyrequire,
|
||||||
yanked_reason=yanked_reason,
|
yanked_reason=yanked_reason,
|
||||||
hashes=hashes,
|
hashes=hashes,
|
||||||
dist_info_metadata=dist_info_metadata,
|
metadata_file_data=metadata_file_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -298,14 +327,39 @@ class Link(KeyBasedCompareMixin):
|
||||||
url = _ensure_quoted_url(urllib.parse.urljoin(base_url, href))
|
url = _ensure_quoted_url(urllib.parse.urljoin(base_url, href))
|
||||||
pyrequire = anchor_attribs.get("data-requires-python")
|
pyrequire = anchor_attribs.get("data-requires-python")
|
||||||
yanked_reason = anchor_attribs.get("data-yanked")
|
yanked_reason = anchor_attribs.get("data-yanked")
|
||||||
dist_info_metadata = anchor_attribs.get("data-dist-info-metadata")
|
|
||||||
|
# PEP 714: Indexes must use the name data-core-metadata, but
|
||||||
|
# clients should support the old name as a fallback for compatibility.
|
||||||
|
metadata_info = anchor_attribs.get("data-core-metadata")
|
||||||
|
if metadata_info is None:
|
||||||
|
metadata_info = anchor_attribs.get("data-dist-info-metadata")
|
||||||
|
# The metadata info value may be the string "true", or a string of
|
||||||
|
# the form "hashname=hashval"
|
||||||
|
if metadata_info == "true":
|
||||||
|
# The file exists, but there are no hashes
|
||||||
|
metadata_file_data = MetadataFile(None)
|
||||||
|
elif metadata_info is None:
|
||||||
|
# The file does not exist
|
||||||
|
metadata_file_data = None
|
||||||
|
else:
|
||||||
|
# The file exists, and hashes have been supplied
|
||||||
|
hashname, sep, hashval = metadata_info.partition("=")
|
||||||
|
if sep == "=":
|
||||||
|
metadata_file_data = MetadataFile(supported_hashes({hashname: hashval}))
|
||||||
|
else:
|
||||||
|
# Error - data is wrong. Treat as no hashes supplied.
|
||||||
|
logger.debug(
|
||||||
|
"Index returned invalid data-dist-info-metadata value: %s",
|
||||||
|
metadata_info,
|
||||||
|
)
|
||||||
|
metadata_file_data = MetadataFile(None)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
url,
|
url,
|
||||||
comes_from=page_url,
|
comes_from=page_url,
|
||||||
requires_python=pyrequire,
|
requires_python=pyrequire,
|
||||||
yanked_reason=yanked_reason,
|
yanked_reason=yanked_reason,
|
||||||
dist_info_metadata=dist_info_metadata,
|
metadata_file_data=metadata_file_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
@ -407,17 +461,13 @@ class Link(KeyBasedCompareMixin):
|
||||||
return match.group(1)
|
return match.group(1)
|
||||||
|
|
||||||
def metadata_link(self) -> Optional["Link"]:
|
def metadata_link(self) -> Optional["Link"]:
|
||||||
"""Implementation of PEP 658 parsing."""
|
"""Return a link to the associated core metadata file (if any)."""
|
||||||
# Note that Link.from_element() parsing the "data-dist-info-metadata" attribute
|
if self.metadata_file_data is None:
|
||||||
# from an HTML anchor tag is typically how the Link.dist_info_metadata attribute
|
|
||||||
# gets set.
|
|
||||||
if self.dist_info_metadata is None:
|
|
||||||
return None
|
return None
|
||||||
metadata_url = f"{self.url_without_fragment}.metadata"
|
metadata_url = f"{self.url_without_fragment}.metadata"
|
||||||
metadata_link_hash = LinkHash.parse_pep658_hash(self.dist_info_metadata)
|
if self.metadata_file_data.hashes is None:
|
||||||
if metadata_link_hash is None:
|
|
||||||
return Link(metadata_url)
|
return Link(metadata_url)
|
||||||
return Link(metadata_url, hashes=metadata_link_hash.as_dict())
|
return Link(metadata_url, hashes=self.metadata_file_data.hashes)
|
||||||
|
|
||||||
def as_hashes(self) -> Hashes:
|
def as_hashes(self) -> Hashes:
|
||||||
return Hashes({k: [v] for k, v in self._hashes.items()})
|
return Hashes({k: [v] for k, v in self._hashes.items()})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Set, Tuple
|
||||||
|
|
||||||
from pip._vendor.packaging.tags import Tag
|
from pip._vendor.packaging.tags import Tag
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ class TargetPython:
|
||||||
"py_version",
|
"py_version",
|
||||||
"py_version_info",
|
"py_version_info",
|
||||||
"_valid_tags",
|
"_valid_tags",
|
||||||
|
"_valid_tags_set",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -61,8 +62,9 @@ class TargetPython:
|
||||||
self.py_version = py_version
|
self.py_version = py_version
|
||||||
self.py_version_info = py_version_info
|
self.py_version_info = py_version_info
|
||||||
|
|
||||||
# This is used to cache the return value of get_tags().
|
# This is used to cache the return value of get_(un)sorted_tags.
|
||||||
self._valid_tags: Optional[List[Tag]] = None
|
self._valid_tags: Optional[List[Tag]] = None
|
||||||
|
self._valid_tags_set: Optional[Set[Tag]] = None
|
||||||
|
|
||||||
def format_given(self) -> str:
|
def format_given(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -84,7 +86,7 @@ class TargetPython:
|
||||||
f"{key}={value!r}" for key, value in key_values if value is not None
|
f"{key}={value!r}" for key, value in key_values if value is not None
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_tags(self) -> List[Tag]:
|
def get_sorted_tags(self) -> List[Tag]:
|
||||||
"""
|
"""
|
||||||
Return the supported PEP 425 tags to check wheel candidates against.
|
Return the supported PEP 425 tags to check wheel candidates against.
|
||||||
|
|
||||||
|
@ -108,3 +110,13 @@ class TargetPython:
|
||||||
self._valid_tags = tags
|
self._valid_tags = tags
|
||||||
|
|
||||||
return self._valid_tags
|
return self._valid_tags
|
||||||
|
|
||||||
|
def get_unsorted_tags(self) -> Set[Tag]:
|
||||||
|
"""Exactly the same as get_sorted_tags, but returns a set.
|
||||||
|
|
||||||
|
This is important for performance.
|
||||||
|
"""
|
||||||
|
if self._valid_tags_set is None:
|
||||||
|
self._valid_tags_set = set(self.get_sorted_tags())
|
||||||
|
|
||||||
|
return self._valid_tags_set
|
||||||
|
|
|
@ -514,7 +514,9 @@ class MultiDomainBasicAuth(AuthBase):
|
||||||
|
|
||||||
# Consume content and release the original connection to allow our new
|
# Consume content and release the original connection to allow our new
|
||||||
# request to reuse the same one.
|
# request to reuse the same one.
|
||||||
resp.content
|
# The result of the assignment isn't used, it's just needed to consume
|
||||||
|
# the content.
|
||||||
|
_ = resp.content
|
||||||
resp.raw.release_conn()
|
resp.raw.release_conn()
|
||||||
|
|
||||||
# Add our new username and password to the request
|
# Add our new username and password to the request
|
||||||
|
|
|
@ -419,15 +419,17 @@ class PipSession(requests.Session):
|
||||||
msg += f" (from {source})"
|
msg += f" (from {source})"
|
||||||
logger.info(msg)
|
logger.info(msg)
|
||||||
|
|
||||||
host_port = parse_netloc(host)
|
parsed_host, parsed_port = parse_netloc(host)
|
||||||
if host_port not in self.pip_trusted_origins:
|
if parsed_host is None:
|
||||||
self.pip_trusted_origins.append(host_port)
|
raise ValueError(f"Trusted host URL must include a host part: {host!r}")
|
||||||
|
if (parsed_host, parsed_port) not in self.pip_trusted_origins:
|
||||||
|
self.pip_trusted_origins.append((parsed_host, parsed_port))
|
||||||
|
|
||||||
self.mount(
|
self.mount(
|
||||||
build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter
|
build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter
|
||||||
)
|
)
|
||||||
self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter)
|
self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter)
|
||||||
if not host_port[1]:
|
if not parsed_port:
|
||||||
self.mount(
|
self.mount(
|
||||||
build_url_from_netloc(host, scheme="http") + ":",
|
build_url_from_netloc(host, scheme="http") + ":",
|
||||||
self._trusted_host_adapter,
|
self._trusted_host_adapter,
|
||||||
|
|
|
@ -51,10 +51,22 @@ def get_build_tracker() -> Generator["BuildTracker", None, None]:
|
||||||
yield tracker
|
yield tracker
|
||||||
|
|
||||||
|
|
||||||
|
class TrackerId(str):
|
||||||
|
"""Uniquely identifying string provided to the build tracker."""
|
||||||
|
|
||||||
|
|
||||||
class BuildTracker:
|
class BuildTracker:
|
||||||
|
"""Ensure that an sdist cannot request itself as a setup requirement.
|
||||||
|
|
||||||
|
When an sdist is prepared, it identifies its setup requirements in the
|
||||||
|
context of ``BuildTracker.track()``. If a requirement shows up recursively, this
|
||||||
|
raises an exception.
|
||||||
|
|
||||||
|
This stops fork bombs embedded in malicious packages."""
|
||||||
|
|
||||||
def __init__(self, root: str) -> None:
|
def __init__(self, root: str) -> None:
|
||||||
self._root = root
|
self._root = root
|
||||||
self._entries: Set[InstallRequirement] = set()
|
self._entries: Dict[TrackerId, InstallRequirement] = {}
|
||||||
logger.debug("Created build tracker: %s", self._root)
|
logger.debug("Created build tracker: %s", self._root)
|
||||||
|
|
||||||
def __enter__(self) -> "BuildTracker":
|
def __enter__(self) -> "BuildTracker":
|
||||||
|
@ -69,16 +81,15 @@ class BuildTracker:
|
||||||
) -> None:
|
) -> None:
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
|
||||||
def _entry_path(self, link: Link) -> str:
|
def _entry_path(self, key: TrackerId) -> str:
|
||||||
hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest()
|
hashed = hashlib.sha224(key.encode()).hexdigest()
|
||||||
return os.path.join(self._root, hashed)
|
return os.path.join(self._root, hashed)
|
||||||
|
|
||||||
def add(self, req: InstallRequirement) -> None:
|
def add(self, req: InstallRequirement, key: TrackerId) -> None:
|
||||||
"""Add an InstallRequirement to build tracking."""
|
"""Add an InstallRequirement to build tracking."""
|
||||||
|
|
||||||
assert req.link
|
|
||||||
# Get the file to write information about this requirement.
|
# Get the file to write information about this requirement.
|
||||||
entry_path = self._entry_path(req.link)
|
entry_path = self._entry_path(key)
|
||||||
|
|
||||||
# Try reading from the file. If it exists and can be read from, a build
|
# Try reading from the file. If it exists and can be read from, a build
|
||||||
# is already in progress, so a LookupError is raised.
|
# is already in progress, so a LookupError is raised.
|
||||||
|
@ -92,33 +103,37 @@ class BuildTracker:
|
||||||
raise LookupError(message)
|
raise LookupError(message)
|
||||||
|
|
||||||
# If we're here, req should really not be building already.
|
# If we're here, req should really not be building already.
|
||||||
assert req not in self._entries
|
assert key not in self._entries
|
||||||
|
|
||||||
# Start tracking this requirement.
|
# Start tracking this requirement.
|
||||||
with open(entry_path, "w", encoding="utf-8") as fp:
|
with open(entry_path, "w", encoding="utf-8") as fp:
|
||||||
fp.write(str(req))
|
fp.write(str(req))
|
||||||
self._entries.add(req)
|
self._entries[key] = req
|
||||||
|
|
||||||
logger.debug("Added %s to build tracker %r", req, self._root)
|
logger.debug("Added %s to build tracker %r", req, self._root)
|
||||||
|
|
||||||
def remove(self, req: InstallRequirement) -> None:
|
def remove(self, req: InstallRequirement, key: TrackerId) -> None:
|
||||||
"""Remove an InstallRequirement from build tracking."""
|
"""Remove an InstallRequirement from build tracking."""
|
||||||
|
|
||||||
assert req.link
|
# Delete the created file and the corresponding entry.
|
||||||
# Delete the created file and the corresponding entries.
|
os.unlink(self._entry_path(key))
|
||||||
os.unlink(self._entry_path(req.link))
|
del self._entries[key]
|
||||||
self._entries.remove(req)
|
|
||||||
|
|
||||||
logger.debug("Removed %s from build tracker %r", req, self._root)
|
logger.debug("Removed %s from build tracker %r", req, self._root)
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
for req in set(self._entries):
|
for key, req in list(self._entries.items()):
|
||||||
self.remove(req)
|
self.remove(req, key)
|
||||||
|
|
||||||
logger.debug("Removed build tracker: %r", self._root)
|
logger.debug("Removed build tracker: %r", self._root)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def track(self, req: InstallRequirement) -> Generator[None, None, None]:
|
def track(self, req: InstallRequirement, key: str) -> Generator[None, None, None]:
|
||||||
self.add(req)
|
"""Ensure that `key` cannot install itself as a setup requirement.
|
||||||
|
|
||||||
|
:raises LookupError: If `key` was already provided in a parent invocation of
|
||||||
|
the context introduced by this method."""
|
||||||
|
tracker_id = TrackerId(key)
|
||||||
|
self.add(req, tracker_id)
|
||||||
yield
|
yield
|
||||||
self.remove(req)
|
self.remove(req, tracker_id)
|
||||||
|
|
|
@ -5,12 +5,15 @@ import logging
|
||||||
from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple
|
from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple
|
||||||
|
|
||||||
from pip._vendor.packaging.requirements import Requirement
|
from pip._vendor.packaging.requirements import Requirement
|
||||||
|
from pip._vendor.packaging.specifiers import LegacySpecifier
|
||||||
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
||||||
|
from pip._vendor.packaging.version import LegacyVersion
|
||||||
|
|
||||||
from pip._internal.distributions import make_distribution_for_install_requirement
|
from pip._internal.distributions import make_distribution_for_install_requirement
|
||||||
from pip._internal.metadata import get_default_environment
|
from pip._internal.metadata import get_default_environment
|
||||||
from pip._internal.metadata.base import DistributionVersion
|
from pip._internal.metadata.base import DistributionVersion
|
||||||
from pip._internal.req.req_install import InstallRequirement
|
from pip._internal.req.req_install import InstallRequirement
|
||||||
|
from pip._internal.utils.deprecation import deprecated
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -57,6 +60,8 @@ def check_package_set(
|
||||||
package name and returns a boolean.
|
package name and returns a boolean.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
warn_legacy_versions_and_specifiers(package_set)
|
||||||
|
|
||||||
missing = {}
|
missing = {}
|
||||||
conflicting = {}
|
conflicting = {}
|
||||||
|
|
||||||
|
@ -147,3 +152,36 @@ def _create_whitelist(
|
||||||
break
|
break
|
||||||
|
|
||||||
return packages_affected
|
return packages_affected
|
||||||
|
|
||||||
|
|
||||||
|
def warn_legacy_versions_and_specifiers(package_set: PackageSet) -> None:
|
||||||
|
for project_name, package_details in package_set.items():
|
||||||
|
if isinstance(package_details.version, LegacyVersion):
|
||||||
|
deprecated(
|
||||||
|
reason=(
|
||||||
|
f"{project_name} {package_details.version} "
|
||||||
|
f"has a non-standard version number."
|
||||||
|
),
|
||||||
|
replacement=(
|
||||||
|
f"to upgrade to a newer version of {project_name} "
|
||||||
|
f"or contact the author to suggest that they "
|
||||||
|
f"release a version with a conforming version number"
|
||||||
|
),
|
||||||
|
issue=12063,
|
||||||
|
gone_in="23.3",
|
||||||
|
)
|
||||||
|
for dep in package_details.dependencies:
|
||||||
|
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
|
||||||
|
deprecated(
|
||||||
|
reason=(
|
||||||
|
f"{project_name} {package_details.version} "
|
||||||
|
f"has a non-standard dependency specifier {dep}."
|
||||||
|
),
|
||||||
|
replacement=(
|
||||||
|
f"to upgrade to a newer version of {project_name} "
|
||||||
|
f"or contact the author to suggest that they "
|
||||||
|
f"release a version with a conforming dependency specifiers"
|
||||||
|
),
|
||||||
|
issue=12063,
|
||||||
|
gone_in="23.3",
|
||||||
|
)
|
||||||
|
|
|
@ -267,9 +267,9 @@ def get_csv_rows_for_installed(
|
||||||
path = _fs_to_record_path(f, lib_dir)
|
path = _fs_to_record_path(f, lib_dir)
|
||||||
digest, length = rehash(f)
|
digest, length = rehash(f)
|
||||||
installed_rows.append((path, digest, length))
|
installed_rows.append((path, digest, length))
|
||||||
for installed_record_path in installed.values():
|
return installed_rows + [
|
||||||
installed_rows.append((installed_record_path, "", ""))
|
(installed_record_path, "", "") for installed_record_path in installed.values()
|
||||||
return installed_rows
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_console_script_specs(console: Dict[str, str]) -> List[str]:
|
def get_console_script_specs(console: Dict[str, str]) -> List[str]:
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
# The following comment should be removed at some point in the future.
|
# The following comment should be removed at some point in the future.
|
||||||
# mypy: strict-optional=False
|
# mypy: strict-optional=False
|
||||||
|
|
||||||
import logging
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict, Iterable, List, Optional
|
from typing import Dict, Iterable, List, Optional
|
||||||
|
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
|
@ -21,7 +21,6 @@ from pip._internal.exceptions import (
|
||||||
InstallationError,
|
InstallationError,
|
||||||
MetadataInconsistent,
|
MetadataInconsistent,
|
||||||
NetworkConnectionError,
|
NetworkConnectionError,
|
||||||
PreviousBuildDirError,
|
|
||||||
VcsHashUnsupported,
|
VcsHashUnsupported,
|
||||||
)
|
)
|
||||||
from pip._internal.index.package_finder import PackageFinder
|
from pip._internal.index.package_finder import PackageFinder
|
||||||
|
@ -37,6 +36,7 @@ from pip._internal.network.lazy_wheel import (
|
||||||
from pip._internal.network.session import PipSession
|
from pip._internal.network.session import PipSession
|
||||||
from pip._internal.operations.build.build_tracker import BuildTracker
|
from pip._internal.operations.build.build_tracker import BuildTracker
|
||||||
from pip._internal.req.req_install import InstallRequirement
|
from pip._internal.req.req_install import InstallRequirement
|
||||||
|
from pip._internal.utils._log import getLogger
|
||||||
from pip._internal.utils.direct_url_helpers import (
|
from pip._internal.utils.direct_url_helpers import (
|
||||||
direct_url_for_editable,
|
direct_url_for_editable,
|
||||||
direct_url_from_link,
|
direct_url_from_link,
|
||||||
|
@ -47,13 +47,12 @@ from pip._internal.utils.misc import (
|
||||||
display_path,
|
display_path,
|
||||||
hash_file,
|
hash_file,
|
||||||
hide_url,
|
hide_url,
|
||||||
is_installable_dir,
|
|
||||||
)
|
)
|
||||||
from pip._internal.utils.temp_dir import TempDirectory
|
from pip._internal.utils.temp_dir import TempDirectory
|
||||||
from pip._internal.utils.unpacking import unpack_file
|
from pip._internal.utils.unpacking import unpack_file
|
||||||
from pip._internal.vcs import vcs
|
from pip._internal.vcs import vcs
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _get_prepared_distribution(
|
def _get_prepared_distribution(
|
||||||
|
@ -65,10 +64,12 @@ def _get_prepared_distribution(
|
||||||
) -> BaseDistribution:
|
) -> BaseDistribution:
|
||||||
"""Prepare a distribution for installation."""
|
"""Prepare a distribution for installation."""
|
||||||
abstract_dist = make_distribution_for_install_requirement(req)
|
abstract_dist = make_distribution_for_install_requirement(req)
|
||||||
with build_tracker.track(req):
|
tracker_id = abstract_dist.build_tracker_id
|
||||||
abstract_dist.prepare_distribution_metadata(
|
if tracker_id is not None:
|
||||||
finder, build_isolation, check_build_deps
|
with build_tracker.track(req, tracker_id):
|
||||||
)
|
abstract_dist.prepare_distribution_metadata(
|
||||||
|
finder, build_isolation, check_build_deps
|
||||||
|
)
|
||||||
return abstract_dist.get_metadata_distribution()
|
return abstract_dist.get_metadata_distribution()
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,6 +227,7 @@ class RequirementPreparer:
|
||||||
use_user_site: bool,
|
use_user_site: bool,
|
||||||
lazy_wheel: bool,
|
lazy_wheel: bool,
|
||||||
verbosity: int,
|
verbosity: int,
|
||||||
|
legacy_resolver: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
@ -259,6 +261,9 @@ class RequirementPreparer:
|
||||||
# How verbose should underlying tooling be?
|
# How verbose should underlying tooling be?
|
||||||
self.verbosity = verbosity
|
self.verbosity = verbosity
|
||||||
|
|
||||||
|
# Are we using the legacy resolver?
|
||||||
|
self.legacy_resolver = legacy_resolver
|
||||||
|
|
||||||
# Memoized downloaded files, as mapping of url: path.
|
# Memoized downloaded files, as mapping of url: path.
|
||||||
self._downloaded: Dict[str, str] = {}
|
self._downloaded: Dict[str, str] = {}
|
||||||
|
|
||||||
|
@ -313,21 +318,7 @@ class RequirementPreparer:
|
||||||
autodelete=True,
|
autodelete=True,
|
||||||
parallel_builds=parallel_builds,
|
parallel_builds=parallel_builds,
|
||||||
)
|
)
|
||||||
|
req.ensure_pristine_source_checkout()
|
||||||
# If a checkout exists, it's unwise to keep going. version
|
|
||||||
# inconsistencies are logged later, but do not fail the
|
|
||||||
# installation.
|
|
||||||
# FIXME: this won't upgrade when there's an existing
|
|
||||||
# package unpacked in `req.source_dir`
|
|
||||||
# TODO: this check is now probably dead code
|
|
||||||
if is_installable_dir(req.source_dir):
|
|
||||||
raise PreviousBuildDirError(
|
|
||||||
"pip can't proceed with requirements '{}' due to a"
|
|
||||||
"pre-existing build directory ({}). This is likely "
|
|
||||||
"due to a previous installation that failed . pip is "
|
|
||||||
"being responsible and not assuming it can delete this. "
|
|
||||||
"Please delete it and try again.".format(req, req.source_dir)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes:
|
def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes:
|
||||||
# By the time this is called, the requirement's link should have
|
# By the time this is called, the requirement's link should have
|
||||||
|
@ -352,7 +343,7 @@ class RequirementPreparer:
|
||||||
# a surprising hash mismatch in the future.
|
# a surprising hash mismatch in the future.
|
||||||
# file:/// URLs aren't pinnable, so don't complain about them
|
# file:/// URLs aren't pinnable, so don't complain about them
|
||||||
# not being pinned.
|
# not being pinned.
|
||||||
if req.original_link is None and not req.is_pinned:
|
if not req.is_direct and not req.is_pinned:
|
||||||
raise HashUnpinned()
|
raise HashUnpinned()
|
||||||
|
|
||||||
# If known-good hashes are missing for this requirement,
|
# If known-good hashes are missing for this requirement,
|
||||||
|
@ -365,6 +356,11 @@ class RequirementPreparer:
|
||||||
self,
|
self,
|
||||||
req: InstallRequirement,
|
req: InstallRequirement,
|
||||||
) -> Optional[BaseDistribution]:
|
) -> Optional[BaseDistribution]:
|
||||||
|
if self.legacy_resolver:
|
||||||
|
logger.debug(
|
||||||
|
"Metadata-only fetching is not used in the legacy resolver",
|
||||||
|
)
|
||||||
|
return None
|
||||||
if self.require_hashes:
|
if self.require_hashes:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Metadata-only fetching is not used as hash checking is required",
|
"Metadata-only fetching is not used as hash checking is required",
|
||||||
|
@ -385,7 +381,7 @@ class RequirementPreparer:
|
||||||
if metadata_link is None:
|
if metadata_link is None:
|
||||||
return None
|
return None
|
||||||
assert req.req is not None
|
assert req.req is not None
|
||||||
logger.info(
|
logger.verbose(
|
||||||
"Obtaining dependency information for %s from %s",
|
"Obtaining dependency information for %s from %s",
|
||||||
req.req,
|
req.req,
|
||||||
metadata_link,
|
metadata_link,
|
||||||
|
@ -410,7 +406,7 @@ class RequirementPreparer:
|
||||||
# NB: raw_name will fall back to the name from the install requirement if
|
# NB: raw_name will fall back to the name from the install requirement if
|
||||||
# the Name: field is not present, but it's noted in the raw_name docstring
|
# the Name: field is not present, but it's noted in the raw_name docstring
|
||||||
# that that should NEVER happen anyway.
|
# that that should NEVER happen anyway.
|
||||||
if metadata_dist.raw_name != req.req.name:
|
if canonicalize_name(metadata_dist.raw_name) != canonicalize_name(req.req.name):
|
||||||
raise MetadataInconsistent(
|
raise MetadataInconsistent(
|
||||||
req, "Name", req.req.name, metadata_dist.raw_name
|
req, "Name", req.req.name, metadata_dist.raw_name
|
||||||
)
|
)
|
||||||
|
@ -470,7 +466,19 @@ class RequirementPreparer:
|
||||||
for link, (filepath, _) in batch_download:
|
for link, (filepath, _) in batch_download:
|
||||||
logger.debug("Downloading link %s to %s", link, filepath)
|
logger.debug("Downloading link %s to %s", link, filepath)
|
||||||
req = links_to_fully_download[link]
|
req = links_to_fully_download[link]
|
||||||
|
# Record the downloaded file path so wheel reqs can extract a Distribution
|
||||||
|
# in .get_dist().
|
||||||
req.local_file_path = filepath
|
req.local_file_path = filepath
|
||||||
|
# Record that the file is downloaded so we don't do it again in
|
||||||
|
# _prepare_linked_requirement().
|
||||||
|
self._downloaded[req.link.url] = filepath
|
||||||
|
|
||||||
|
# If this is an sdist, we need to unpack it after downloading, but the
|
||||||
|
# .source_dir won't be set up until we are in _prepare_linked_requirement().
|
||||||
|
# Add the downloaded archive to the install requirement to unpack after
|
||||||
|
# preparing the source dir.
|
||||||
|
if not req.is_wheel:
|
||||||
|
req.needs_unpacked_archive(Path(filepath))
|
||||||
|
|
||||||
# This step is necessary to ensure all lazy wheels are processed
|
# This step is necessary to ensure all lazy wheels are processed
|
||||||
# successfully by the 'download', 'wheel', and 'install' commands.
|
# successfully by the 'download', 'wheel', and 'install' commands.
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# The following comment should be removed at some point in the future.
|
|
||||||
# mypy: strict-optional=False
|
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -9,6 +6,7 @@ import sys
|
||||||
import uuid
|
import uuid
|
||||||
import zipfile
|
import zipfile
|
||||||
from optparse import Values
|
from optparse import Values
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union
|
from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union
|
||||||
|
|
||||||
from pip._vendor.packaging.markers import Marker
|
from pip._vendor.packaging.markers import Marker
|
||||||
|
@ -20,7 +18,7 @@ from pip._vendor.packaging.version import parse as parse_version
|
||||||
from pip._vendor.pyproject_hooks import BuildBackendHookCaller
|
from pip._vendor.pyproject_hooks import BuildBackendHookCaller
|
||||||
|
|
||||||
from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment
|
from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment
|
||||||
from pip._internal.exceptions import InstallationError
|
from pip._internal.exceptions import InstallationError, PreviousBuildDirError
|
||||||
from pip._internal.locations import get_scheme
|
from pip._internal.locations import get_scheme
|
||||||
from pip._internal.metadata import (
|
from pip._internal.metadata import (
|
||||||
BaseDistribution,
|
BaseDistribution,
|
||||||
|
@ -50,11 +48,13 @@ from pip._internal.utils.misc import (
|
||||||
backup_dir,
|
backup_dir,
|
||||||
display_path,
|
display_path,
|
||||||
hide_url,
|
hide_url,
|
||||||
|
is_installable_dir,
|
||||||
redact_auth_from_url,
|
redact_auth_from_url,
|
||||||
)
|
)
|
||||||
from pip._internal.utils.packaging import safe_extra
|
from pip._internal.utils.packaging import safe_extra
|
||||||
from pip._internal.utils.subprocess import runner_with_spinner_message
|
from pip._internal.utils.subprocess import runner_with_spinner_message
|
||||||
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
|
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
|
||||||
|
from pip._internal.utils.unpacking import unpack_file
|
||||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||||
from pip._internal.vcs import vcs
|
from pip._internal.vcs import vcs
|
||||||
|
|
||||||
|
@ -104,6 +104,8 @@ class InstallRequirement:
|
||||||
if link.is_file:
|
if link.is_file:
|
||||||
self.source_dir = os.path.normpath(os.path.abspath(link.file_path))
|
self.source_dir = os.path.normpath(os.path.abspath(link.file_path))
|
||||||
|
|
||||||
|
# original_link is the direct URL that was provided by the user for the
|
||||||
|
# requirement, either directly or via a constraints file.
|
||||||
if link is None and req and req.url:
|
if link is None and req and req.url:
|
||||||
# PEP 508 URL requirement
|
# PEP 508 URL requirement
|
||||||
link = Link(req.url)
|
link = Link(req.url)
|
||||||
|
@ -181,6 +183,9 @@ class InstallRequirement:
|
||||||
# This requirement needs more preparation before it can be built
|
# This requirement needs more preparation before it can be built
|
||||||
self.needs_more_preparation = False
|
self.needs_more_preparation = False
|
||||||
|
|
||||||
|
# This requirement needs to be unpacked before it can be installed.
|
||||||
|
self._archive_source: Optional[Path] = None
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.req:
|
if self.req:
|
||||||
s = str(self.req)
|
s = str(self.req)
|
||||||
|
@ -242,15 +247,22 @@ class InstallRequirement:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def specifier(self) -> SpecifierSet:
|
def specifier(self) -> SpecifierSet:
|
||||||
|
assert self.req is not None
|
||||||
return self.req.specifier
|
return self.req.specifier
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_direct(self) -> bool:
|
||||||
|
"""Whether this requirement was specified as a direct URL."""
|
||||||
|
return self.original_link is not None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_pinned(self) -> bool:
|
def is_pinned(self) -> bool:
|
||||||
"""Return whether I am pinned to an exact version.
|
"""Return whether I am pinned to an exact version.
|
||||||
|
|
||||||
For example, some-package==1.2 is pinned; some-package>1.2 is not.
|
For example, some-package==1.2 is pinned; some-package>1.2 is not.
|
||||||
"""
|
"""
|
||||||
specifiers = self.specifier
|
assert self.req is not None
|
||||||
|
specifiers = self.req.specifier
|
||||||
return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="}
|
return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="}
|
||||||
|
|
||||||
def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool:
|
def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool:
|
||||||
|
@ -293,11 +305,12 @@ class InstallRequirement:
|
||||||
good_hashes = self.hash_options.copy()
|
good_hashes = self.hash_options.copy()
|
||||||
if trust_internet:
|
if trust_internet:
|
||||||
link = self.link
|
link = self.link
|
||||||
elif self.original_link and self.user_supplied:
|
elif self.is_direct and self.user_supplied:
|
||||||
link = self.original_link
|
link = self.original_link
|
||||||
else:
|
else:
|
||||||
link = None
|
link = None
|
||||||
if link and link.hash:
|
if link and link.hash:
|
||||||
|
assert link.hash_name is not None
|
||||||
good_hashes.setdefault(link.hash_name, []).append(link.hash)
|
good_hashes.setdefault(link.hash_name, []).append(link.hash)
|
||||||
return Hashes(good_hashes)
|
return Hashes(good_hashes)
|
||||||
|
|
||||||
|
@ -307,6 +320,7 @@ class InstallRequirement:
|
||||||
return None
|
return None
|
||||||
s = str(self.req)
|
s = str(self.req)
|
||||||
if self.comes_from:
|
if self.comes_from:
|
||||||
|
comes_from: Optional[str]
|
||||||
if isinstance(self.comes_from, str):
|
if isinstance(self.comes_from, str):
|
||||||
comes_from = self.comes_from
|
comes_from = self.comes_from
|
||||||
else:
|
else:
|
||||||
|
@ -338,7 +352,7 @@ class InstallRequirement:
|
||||||
|
|
||||||
# When parallel builds are enabled, add a UUID to the build directory
|
# When parallel builds are enabled, add a UUID to the build directory
|
||||||
# name so multiple builds do not interfere with each other.
|
# name so multiple builds do not interfere with each other.
|
||||||
dir_name: str = canonicalize_name(self.name)
|
dir_name: str = canonicalize_name(self.req.name)
|
||||||
if parallel_builds:
|
if parallel_builds:
|
||||||
dir_name = f"{dir_name}_{uuid.uuid4().hex}"
|
dir_name = f"{dir_name}_{uuid.uuid4().hex}"
|
||||||
|
|
||||||
|
@ -381,6 +395,7 @@ class InstallRequirement:
|
||||||
)
|
)
|
||||||
|
|
||||||
def warn_on_mismatching_name(self) -> None:
|
def warn_on_mismatching_name(self) -> None:
|
||||||
|
assert self.req is not None
|
||||||
metadata_name = canonicalize_name(self.metadata["Name"])
|
metadata_name = canonicalize_name(self.metadata["Name"])
|
||||||
if canonicalize_name(self.req.name) == metadata_name:
|
if canonicalize_name(self.req.name) == metadata_name:
|
||||||
# Everything is fine.
|
# Everything is fine.
|
||||||
|
@ -450,6 +465,7 @@ class InstallRequirement:
|
||||||
# Things valid for sdists
|
# Things valid for sdists
|
||||||
@property
|
@property
|
||||||
def unpacked_source_directory(self) -> str:
|
def unpacked_source_directory(self) -> str:
|
||||||
|
assert self.source_dir, f"No source dir for {self}"
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
self.source_dir, self.link and self.link.subdirectory_fragment or ""
|
self.source_dir, self.link and self.link.subdirectory_fragment or ""
|
||||||
)
|
)
|
||||||
|
@ -536,7 +552,7 @@ class InstallRequirement:
|
||||||
Under PEP 517 and PEP 660, call the backend hook to prepare the metadata.
|
Under PEP 517 and PEP 660, call the backend hook to prepare the metadata.
|
||||||
Under legacy processing, call setup.py egg-info.
|
Under legacy processing, call setup.py egg-info.
|
||||||
"""
|
"""
|
||||||
assert self.source_dir
|
assert self.source_dir, f"No source dir for {self}"
|
||||||
details = self.name or f"from {self.link}"
|
details = self.name or f"from {self.link}"
|
||||||
|
|
||||||
if self.use_pep517:
|
if self.use_pep517:
|
||||||
|
@ -585,8 +601,10 @@ class InstallRequirement:
|
||||||
if self.metadata_directory:
|
if self.metadata_directory:
|
||||||
return get_directory_distribution(self.metadata_directory)
|
return get_directory_distribution(self.metadata_directory)
|
||||||
elif self.local_file_path and self.is_wheel:
|
elif self.local_file_path and self.is_wheel:
|
||||||
|
assert self.req is not None
|
||||||
return get_wheel_distribution(
|
return get_wheel_distribution(
|
||||||
FilesystemWheel(self.local_file_path), canonicalize_name(self.name)
|
FilesystemWheel(self.local_file_path),
|
||||||
|
canonicalize_name(self.req.name),
|
||||||
)
|
)
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
f"InstallRequirement {self} has no metadata directory and no wheel: "
|
f"InstallRequirement {self} has no metadata directory and no wheel: "
|
||||||
|
@ -594,9 +612,9 @@ class InstallRequirement:
|
||||||
)
|
)
|
||||||
|
|
||||||
def assert_source_matches_version(self) -> None:
|
def assert_source_matches_version(self) -> None:
|
||||||
assert self.source_dir
|
assert self.source_dir, f"No source dir for {self}"
|
||||||
version = self.metadata["version"]
|
version = self.metadata["version"]
|
||||||
if self.req.specifier and version not in self.req.specifier:
|
if self.req and self.req.specifier and version not in self.req.specifier:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Requested %s, but installing version %s",
|
"Requested %s, but installing version %s",
|
||||||
self,
|
self,
|
||||||
|
@ -633,6 +651,27 @@ class InstallRequirement:
|
||||||
parallel_builds=parallel_builds,
|
parallel_builds=parallel_builds,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def needs_unpacked_archive(self, archive_source: Path) -> None:
|
||||||
|
assert self._archive_source is None
|
||||||
|
self._archive_source = archive_source
|
||||||
|
|
||||||
|
def ensure_pristine_source_checkout(self) -> None:
|
||||||
|
"""Ensure the source directory has not yet been built in."""
|
||||||
|
assert self.source_dir is not None
|
||||||
|
if self._archive_source is not None:
|
||||||
|
unpack_file(str(self._archive_source), self.source_dir)
|
||||||
|
elif is_installable_dir(self.source_dir):
|
||||||
|
# If a checkout exists, it's unwise to keep going.
|
||||||
|
# version inconsistencies are logged later, but do not fail
|
||||||
|
# the installation.
|
||||||
|
raise PreviousBuildDirError(
|
||||||
|
f"pip can't proceed with requirements '{self}' due to a "
|
||||||
|
f"pre-existing build directory ({self.source_dir}). This is likely "
|
||||||
|
"due to a previous installation that failed . pip is "
|
||||||
|
"being responsible and not assuming it can delete this. "
|
||||||
|
"Please delete it and try again."
|
||||||
|
)
|
||||||
|
|
||||||
# For editable installations
|
# For editable installations
|
||||||
def update_editable(self) -> None:
|
def update_editable(self) -> None:
|
||||||
if not self.link:
|
if not self.link:
|
||||||
|
@ -689,9 +728,10 @@ class InstallRequirement:
|
||||||
name = name.replace(os.path.sep, "/")
|
name = name.replace(os.path.sep, "/")
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
assert self.req is not None
|
||||||
path = os.path.join(parentdir, path)
|
path = os.path.join(parentdir, path)
|
||||||
name = _clean_zip_name(path, rootdir)
|
name = _clean_zip_name(path, rootdir)
|
||||||
return self.name + "/" + name
|
return self.req.name + "/" + name
|
||||||
|
|
||||||
def archive(self, build_dir: Optional[str]) -> None:
|
def archive(self, build_dir: Optional[str]) -> None:
|
||||||
"""Saves archive to provided build_dir.
|
"""Saves archive to provided build_dir.
|
||||||
|
@ -770,8 +810,9 @@ class InstallRequirement:
|
||||||
use_user_site: bool = False,
|
use_user_site: bool = False,
|
||||||
pycompile: bool = True,
|
pycompile: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
assert self.req is not None
|
||||||
scheme = get_scheme(
|
scheme = get_scheme(
|
||||||
self.name,
|
self.req.name,
|
||||||
user=use_user_site,
|
user=use_user_site,
|
||||||
home=home,
|
home=home,
|
||||||
root=root,
|
root=root,
|
||||||
|
@ -785,7 +826,7 @@ class InstallRequirement:
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
home=home,
|
home=home,
|
||||||
use_user_site=use_user_site,
|
use_user_site=use_user_site,
|
||||||
name=self.name,
|
name=self.req.name,
|
||||||
setup_py_path=self.setup_py_path,
|
setup_py_path=self.setup_py_path,
|
||||||
isolated=self.isolated,
|
isolated=self.isolated,
|
||||||
build_env=self.build_env,
|
build_env=self.build_env,
|
||||||
|
@ -798,13 +839,13 @@ class InstallRequirement:
|
||||||
assert self.local_file_path
|
assert self.local_file_path
|
||||||
|
|
||||||
install_wheel(
|
install_wheel(
|
||||||
self.name,
|
self.req.name,
|
||||||
self.local_file_path,
|
self.local_file_path,
|
||||||
scheme=scheme,
|
scheme=scheme,
|
||||||
req_description=str(self.req),
|
req_description=str(self.req),
|
||||||
pycompile=pycompile,
|
pycompile=pycompile,
|
||||||
warn_script_location=warn_script_location,
|
warn_script_location=warn_script_location,
|
||||||
direct_url=self.download_info if self.original_link else None,
|
direct_url=self.download_info if self.is_direct else None,
|
||||||
requested=self.user_supplied,
|
requested=self.user_supplied,
|
||||||
)
|
)
|
||||||
self.install_succeeded = True
|
self.install_succeeded = True
|
||||||
|
|
|
@ -2,9 +2,12 @@ import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
|
from pip._vendor.packaging.specifiers import LegacySpecifier
|
||||||
from pip._vendor.packaging.utils import canonicalize_name
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
|
from pip._vendor.packaging.version import LegacyVersion
|
||||||
|
|
||||||
from pip._internal.req.req_install import InstallRequirement
|
from pip._internal.req.req_install import InstallRequirement
|
||||||
|
from pip._internal.utils.deprecation import deprecated
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -80,3 +83,37 @@ class RequirementSet:
|
||||||
for install_req in self.all_requirements
|
for install_req in self.all_requirements
|
||||||
if not install_req.constraint and not install_req.satisfied_by
|
if not install_req.constraint and not install_req.satisfied_by
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def warn_legacy_versions_and_specifiers(self) -> None:
|
||||||
|
for req in self.requirements_to_install:
|
||||||
|
version = req.get_dist().version
|
||||||
|
if isinstance(version, LegacyVersion):
|
||||||
|
deprecated(
|
||||||
|
reason=(
|
||||||
|
f"pip has selected the non standard version {version} "
|
||||||
|
f"of {req}. In the future this version will be "
|
||||||
|
f"ignored as it isn't standard compliant."
|
||||||
|
),
|
||||||
|
replacement=(
|
||||||
|
"set or update constraints to select another version "
|
||||||
|
"or contact the package author to fix the version number"
|
||||||
|
),
|
||||||
|
issue=12063,
|
||||||
|
gone_in="23.3",
|
||||||
|
)
|
||||||
|
for dep in req.get_dist().iter_dependencies():
|
||||||
|
if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier):
|
||||||
|
deprecated(
|
||||||
|
reason=(
|
||||||
|
f"pip has selected {req} {version} which has non "
|
||||||
|
f"standard dependency specifier {dep}. "
|
||||||
|
f"In the future this version of {req} will be "
|
||||||
|
f"ignored as it isn't standard compliant."
|
||||||
|
),
|
||||||
|
replacement=(
|
||||||
|
"set or update constraints to select another version "
|
||||||
|
"or contact the package author to fix the version number"
|
||||||
|
),
|
||||||
|
issue=12063,
|
||||||
|
gone_in="23.3",
|
||||||
|
)
|
||||||
|
|
|
@ -274,7 +274,7 @@ class StashedUninstallPathSet:
|
||||||
|
|
||||||
def commit(self) -> None:
|
def commit(self) -> None:
|
||||||
"""Commits the uninstall by removing stashed files."""
|
"""Commits the uninstall by removing stashed files."""
|
||||||
for _, save_dir in self._save_dirs.items():
|
for save_dir in self._save_dirs.values():
|
||||||
save_dir.cleanup()
|
save_dir.cleanup()
|
||||||
self._moves = []
|
self._moves = []
|
||||||
self._save_dirs = {}
|
self._save_dirs = {}
|
||||||
|
|
|
@ -341,6 +341,7 @@ class AlreadyInstalledCandidate(Candidate):
|
||||||
self.dist = dist
|
self.dist = dist
|
||||||
self._ireq = _make_install_req_from_dist(dist, template)
|
self._ireq = _make_install_req_from_dist(dist, template)
|
||||||
self._factory = factory
|
self._factory = factory
|
||||||
|
self._version = None
|
||||||
|
|
||||||
# This is just logging some messages, so we can do it eagerly.
|
# This is just logging some messages, so we can do it eagerly.
|
||||||
# The returned dist would be exactly the same as self.dist because we
|
# The returned dist would be exactly the same as self.dist because we
|
||||||
|
@ -376,7 +377,9 @@ class AlreadyInstalledCandidate(Candidate):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self) -> CandidateVersion:
|
def version(self) -> CandidateVersion:
|
||||||
return self.dist.version
|
if self._version is None:
|
||||||
|
self._version = self.dist.version
|
||||||
|
return self._version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_editable(self) -> bool:
|
def is_editable(self) -> bool:
|
||||||
|
|
|
@ -132,7 +132,7 @@ class Factory:
|
||||||
if not link.is_wheel:
|
if not link.is_wheel:
|
||||||
return
|
return
|
||||||
wheel = Wheel(link.filename)
|
wheel = Wheel(link.filename)
|
||||||
if wheel.supported(self._finder.target_python.get_tags()):
|
if wheel.supported(self._finder.target_python.get_unsorted_tags()):
|
||||||
return
|
return
|
||||||
msg = f"{link.filename} is not a supported wheel on this platform."
|
msg = f"{link.filename} is not a supported wheel on this platform."
|
||||||
raise UnsupportedWheel(msg)
|
raise UnsupportedWheel(msg)
|
||||||
|
@ -605,8 +605,26 @@ class Factory:
|
||||||
|
|
||||||
cands = self._finder.find_all_candidates(req.project_name)
|
cands = self._finder.find_all_candidates(req.project_name)
|
||||||
skipped_by_requires_python = self._finder.requires_python_skipped_reasons()
|
skipped_by_requires_python = self._finder.requires_python_skipped_reasons()
|
||||||
versions = [str(v) for v in sorted({c.version for c in cands})]
|
|
||||||
|
|
||||||
|
versions_set: Set[CandidateVersion] = set()
|
||||||
|
yanked_versions_set: Set[CandidateVersion] = set()
|
||||||
|
for c in cands:
|
||||||
|
is_yanked = c.link.is_yanked if c.link else False
|
||||||
|
if is_yanked:
|
||||||
|
yanked_versions_set.add(c.version)
|
||||||
|
else:
|
||||||
|
versions_set.add(c.version)
|
||||||
|
|
||||||
|
versions = [str(v) for v in sorted(versions_set)]
|
||||||
|
yanked_versions = [str(v) for v in sorted(yanked_versions_set)]
|
||||||
|
|
||||||
|
if yanked_versions:
|
||||||
|
# Saying "version X is yanked" isn't entirely accurate.
|
||||||
|
# https://github.com/pypa/pip/issues/11745#issuecomment-1402805842
|
||||||
|
logger.critical(
|
||||||
|
"Ignored the following yanked versions: %s",
|
||||||
|
", ".join(yanked_versions) or "none",
|
||||||
|
)
|
||||||
if skipped_by_requires_python:
|
if skipped_by_requires_python:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
"Ignored the following versions that require a different python "
|
"Ignored the following versions that require a different python "
|
||||||
|
|
|
@ -20,7 +20,7 @@ class PipReporter(BaseReporter):
|
||||||
"requirements. This could take a while."
|
"requirements. This could take a while."
|
||||||
),
|
),
|
||||||
8: (
|
8: (
|
||||||
"pip is looking at multiple versions of {package_name} to "
|
"pip is still looking at multiple versions of {package_name} to "
|
||||||
"determine which version is compatible with other "
|
"determine which version is compatible with other "
|
||||||
"requirements. This could take a while."
|
"requirements. This could take a while."
|
||||||
),
|
),
|
||||||
|
|
|
@ -159,6 +159,9 @@ class Resolver(BaseResolver):
|
||||||
|
|
||||||
reqs = req_set.all_requirements
|
reqs = req_set.all_requirements
|
||||||
self.factory.preparer.prepare_linked_requirements_more(reqs)
|
self.factory.preparer.prepare_linked_requirements_more(reqs)
|
||||||
|
for req in reqs:
|
||||||
|
req.prepared = True
|
||||||
|
req.needs_more_preparation = False
|
||||||
return req_set
|
return req_set
|
||||||
|
|
||||||
def get_installation_order(
|
def get_installation_order(
|
||||||
|
|
|
@ -28,8 +28,7 @@ from pip._internal.utils.entrypoints import (
|
||||||
from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace
|
from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace
|
||||||
from pip._internal.utils.misc import ensure_dir
|
from pip._internal.utils.misc import ensure_dir
|
||||||
|
|
||||||
_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ"
|
_WEEK = datetime.timedelta(days=7)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -73,12 +72,10 @@ class SelfCheckState:
|
||||||
if "pypi_version" not in self._state:
|
if "pypi_version" not in self._state:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
seven_days_in_seconds = 7 * 24 * 60 * 60
|
|
||||||
|
|
||||||
# Determine if we need to refresh the state
|
# Determine if we need to refresh the state
|
||||||
last_check = datetime.datetime.strptime(self._state["last_check"], _DATE_FMT)
|
last_check = datetime.datetime.fromisoformat(self._state["last_check"])
|
||||||
seconds_since_last_check = (current_time - last_check).total_seconds()
|
time_since_last_check = current_time - last_check
|
||||||
if seconds_since_last_check > seven_days_in_seconds:
|
if time_since_last_check > _WEEK:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self._state["pypi_version"]
|
return self._state["pypi_version"]
|
||||||
|
@ -100,7 +97,7 @@ class SelfCheckState:
|
||||||
# Include the key so it's easy to tell which pip wrote the
|
# Include the key so it's easy to tell which pip wrote the
|
||||||
# file.
|
# file.
|
||||||
"key": self.key,
|
"key": self.key,
|
||||||
"last_check": current_time.strftime(_DATE_FMT),
|
"last_check": current_time.isoformat(),
|
||||||
"pypi_version": pypi_version,
|
"pypi_version": pypi_version,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +226,7 @@ def pip_self_version_check(session: PipSession, options: optparse.Values) -> Non
|
||||||
try:
|
try:
|
||||||
upgrade_prompt = _self_version_check_logic(
|
upgrade_prompt = _self_version_check_logic(
|
||||||
state=SelfCheckState(cache_dir=options.cache_dir),
|
state=SelfCheckState(cache_dir=options.cache_dir),
|
||||||
current_time=datetime.datetime.utcnow(),
|
current_time=datetime.datetime.now(datetime.timezone.utc),
|
||||||
local_version=installed_dist.version,
|
local_version=installed_dist.version,
|
||||||
get_remote_version=functools.partial(
|
get_remote_version=functools.partial(
|
||||||
_get_current_remote_pip_version, session, options
|
_get_current_remote_pip_version, session, options
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
# The following comment should be removed at some point in the future.
|
|
||||||
# mypy: strict-optional=False
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
@ -20,8 +17,11 @@ def glibc_version_string_confstr() -> Optional[str]:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
|
gnu_libc_version = os.confstr("CS_GNU_LIBC_VERSION")
|
||||||
|
if gnu_libc_version is None:
|
||||||
|
return None
|
||||||
# os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17":
|
# os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17":
|
||||||
_, version = os.confstr("CS_GNU_LIBC_VERSION").split()
|
_, version = gnu_libc_version.split()
|
||||||
except (AttributeError, OSError, ValueError):
|
except (AttributeError, OSError, ValueError):
|
||||||
# os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
|
# os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
"""A helper module that injects SecureTransport, on import.
|
|
||||||
|
|
||||||
The import should be done as early as possible, to ensure all requests and
|
|
||||||
sessions (or whatever) are created after injecting SecureTransport.
|
|
||||||
|
|
||||||
Note that we only do the injection on macOS, when the linked OpenSSL is too
|
|
||||||
old to handle TLSv1.2.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def inject_securetransport() -> None:
|
|
||||||
# Only relevant on macOS
|
|
||||||
if sys.platform != "darwin":
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
import ssl
|
|
||||||
except ImportError:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Checks for OpenSSL 1.0.1
|
|
||||||
if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100F:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
from pip._vendor.urllib3.contrib import securetransport
|
|
||||||
except (ImportError, OSError):
|
|
||||||
return
|
|
||||||
|
|
||||||
securetransport.inject_into_urllib3()
|
|
||||||
|
|
||||||
|
|
||||||
inject_securetransport()
|
|
|
@ -1,6 +1,3 @@
|
||||||
# The following comment should be removed at some point in the future.
|
|
||||||
# mypy: strict-optional=False
|
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import errno
|
import errno
|
||||||
import getpass
|
import getpass
|
||||||
|
@ -14,9 +11,11 @@ import stat
|
||||||
import sys
|
import sys
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
from functools import partial
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from itertools import filterfalse, tee, zip_longest
|
from itertools import filterfalse, tee, zip_longest
|
||||||
from types import TracebackType
|
from pathlib import Path
|
||||||
|
from types import FunctionType, TracebackType
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
BinaryIO,
|
BinaryIO,
|
||||||
|
@ -69,6 +68,8 @@ T = TypeVar("T")
|
||||||
ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
|
ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
|
||||||
VersionInfo = Tuple[int, int, int]
|
VersionInfo = Tuple[int, int, int]
|
||||||
NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]]
|
NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]]
|
||||||
|
OnExc = Callable[[FunctionType, Path, BaseException], Any]
|
||||||
|
OnErr = Callable[[FunctionType, Path, ExcInfo], Any]
|
||||||
|
|
||||||
|
|
||||||
def get_pip_version() -> str:
|
def get_pip_version() -> str:
|
||||||
|
@ -126,28 +127,75 @@ def get_prog() -> str:
|
||||||
# Retry every half second for up to 3 seconds
|
# Retry every half second for up to 3 seconds
|
||||||
# Tenacity raises RetryError by default, explicitly raise the original exception
|
# Tenacity raises RetryError by default, explicitly raise the original exception
|
||||||
@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
|
@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
|
||||||
def rmtree(dir: str, ignore_errors: bool = False) -> None:
|
def rmtree(
|
||||||
shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler)
|
dir: str,
|
||||||
|
ignore_errors: bool = False,
|
||||||
|
onexc: Optional[OnExc] = None,
|
||||||
|
) -> None:
|
||||||
|
if ignore_errors:
|
||||||
|
onexc = _onerror_ignore
|
||||||
|
if onexc is None:
|
||||||
|
onexc = _onerror_reraise
|
||||||
|
handler: OnErr = partial(
|
||||||
|
# `[func, path, Union[ExcInfo, BaseException]] -> Any` is equivalent to
|
||||||
|
# `Union[([func, path, ExcInfo] -> Any), ([func, path, BaseException] -> Any)]`.
|
||||||
|
cast(Union[OnExc, OnErr], rmtree_errorhandler),
|
||||||
|
onexc=onexc,
|
||||||
|
)
|
||||||
|
if sys.version_info >= (3, 12):
|
||||||
|
# See https://docs.python.org/3.12/whatsnew/3.12.html#shutil.
|
||||||
|
shutil.rmtree(dir, onexc=handler)
|
||||||
|
else:
|
||||||
|
shutil.rmtree(dir, onerror=handler)
|
||||||
|
|
||||||
|
|
||||||
def rmtree_errorhandler(func: Callable[..., Any], path: str, exc_info: ExcInfo) -> None:
|
def _onerror_ignore(*_args: Any) -> None:
|
||||||
"""On Windows, the files in .svn are read-only, so when rmtree() tries to
|
pass
|
||||||
remove them, an exception is thrown. We catch that here, remove the
|
|
||||||
read-only attribute, and hopefully continue without problems."""
|
|
||||||
|
def _onerror_reraise(*_args: Any) -> None:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def rmtree_errorhandler(
|
||||||
|
func: FunctionType,
|
||||||
|
path: Path,
|
||||||
|
exc_info: Union[ExcInfo, BaseException],
|
||||||
|
*,
|
||||||
|
onexc: OnExc = _onerror_reraise,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
`rmtree` error handler to 'force' a file remove (i.e. like `rm -f`).
|
||||||
|
|
||||||
|
* If a file is readonly then it's write flag is set and operation is
|
||||||
|
retried.
|
||||||
|
|
||||||
|
* `onerror` is the original callback from `rmtree(... onerror=onerror)`
|
||||||
|
that is chained at the end if the "rm -f" still fails.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE)
|
st_mode = os.stat(path).st_mode
|
||||||
except OSError:
|
except OSError:
|
||||||
# it's equivalent to os.path.exists
|
# it's equivalent to os.path.exists
|
||||||
return
|
return
|
||||||
|
|
||||||
if has_attr_readonly:
|
if not st_mode & stat.S_IWRITE:
|
||||||
# convert to read/write
|
# convert to read/write
|
||||||
os.chmod(path, stat.S_IWRITE)
|
try:
|
||||||
# use the original function to repeat the operation
|
os.chmod(path, st_mode | stat.S_IWRITE)
|
||||||
func(path)
|
except OSError:
|
||||||
return
|
pass
|
||||||
else:
|
else:
|
||||||
raise
|
# use the original function to repeat the operation
|
||||||
|
try:
|
||||||
|
func(path)
|
||||||
|
return
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not isinstance(exc_info, BaseException):
|
||||||
|
_, exc_info, _ = exc_info
|
||||||
|
onexc(func, path, exc_info)
|
||||||
|
|
||||||
|
|
||||||
def display_path(path: str) -> str:
|
def display_path(path: str) -> str:
|
||||||
|
@ -339,17 +387,18 @@ def write_output(msg: Any, *args: Any) -> None:
|
||||||
|
|
||||||
|
|
||||||
class StreamWrapper(StringIO):
|
class StreamWrapper(StringIO):
|
||||||
orig_stream: TextIO = None
|
orig_stream: TextIO
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper":
|
def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper":
|
||||||
cls.orig_stream = orig_stream
|
ret = cls()
|
||||||
return cls()
|
ret.orig_stream = orig_stream
|
||||||
|
return ret
|
||||||
|
|
||||||
# compileall.compile_dir() needs stdout.encoding to print to stdout
|
# compileall.compile_dir() needs stdout.encoding to print to stdout
|
||||||
# https://github.com/python/mypy/issues/4125
|
# type ignore is because TextIOBase.encoding is writeable
|
||||||
@property
|
@property
|
||||||
def encoding(self): # type: ignore
|
def encoding(self) -> str: # type: ignore
|
||||||
return self.orig_stream.encoding
|
return self.orig_stream.encoding
|
||||||
|
|
||||||
|
|
||||||
|
@ -417,7 +466,7 @@ def build_url_from_netloc(netloc: str, scheme: str = "https") -> str:
|
||||||
return f"{scheme}://{netloc}"
|
return f"{scheme}://{netloc}"
|
||||||
|
|
||||||
|
|
||||||
def parse_netloc(netloc: str) -> Tuple[str, Optional[int]]:
|
def parse_netloc(netloc: str) -> Tuple[Optional[str], Optional[int]]:
|
||||||
"""
|
"""
|
||||||
Return the host-port pair from a netloc.
|
Return the host-port pair from a netloc.
|
||||||
"""
|
"""
|
||||||
|
@ -505,7 +554,9 @@ def _redact_netloc(netloc: str) -> Tuple[str]:
|
||||||
return (redact_netloc(netloc),)
|
return (redact_netloc(netloc),)
|
||||||
|
|
||||||
|
|
||||||
def split_auth_netloc_from_url(url: str) -> Tuple[str, str, Tuple[str, str]]:
|
def split_auth_netloc_from_url(
|
||||||
|
url: str,
|
||||||
|
) -> Tuple[str, str, Tuple[Optional[str], Optional[str]]]:
|
||||||
"""
|
"""
|
||||||
Parse a url into separate netloc, auth, and url with no auth.
|
Parse a url into separate netloc, auth, and url with no auth.
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,19 @@ import itertools
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import traceback
|
||||||
from contextlib import ExitStack, contextmanager
|
from contextlib import ExitStack, contextmanager
|
||||||
from typing import Any, Dict, Generator, Optional, TypeVar, Union
|
from pathlib import Path
|
||||||
|
from types import FunctionType
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
Generator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
from pip._internal.utils.misc import enum, rmtree
|
from pip._internal.utils.misc import enum, rmtree
|
||||||
|
|
||||||
|
@ -106,6 +117,7 @@ class TempDirectory:
|
||||||
delete: Union[bool, None, _Default] = _default,
|
delete: Union[bool, None, _Default] = _default,
|
||||||
kind: str = "temp",
|
kind: str = "temp",
|
||||||
globally_managed: bool = False,
|
globally_managed: bool = False,
|
||||||
|
ignore_cleanup_errors: bool = True,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
@ -128,6 +140,7 @@ class TempDirectory:
|
||||||
self._deleted = False
|
self._deleted = False
|
||||||
self.delete = delete
|
self.delete = delete
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
|
self.ignore_cleanup_errors = ignore_cleanup_errors
|
||||||
|
|
||||||
if globally_managed:
|
if globally_managed:
|
||||||
assert _tempdir_manager is not None
|
assert _tempdir_manager is not None
|
||||||
|
@ -170,7 +183,44 @@ class TempDirectory:
|
||||||
self._deleted = True
|
self._deleted = True
|
||||||
if not os.path.exists(self._path):
|
if not os.path.exists(self._path):
|
||||||
return
|
return
|
||||||
rmtree(self._path)
|
|
||||||
|
errors: List[BaseException] = []
|
||||||
|
|
||||||
|
def onerror(
|
||||||
|
func: FunctionType,
|
||||||
|
path: Path,
|
||||||
|
exc_val: BaseException,
|
||||||
|
) -> None:
|
||||||
|
"""Log a warning for a `rmtree` error and continue"""
|
||||||
|
formatted_exc = "\n".join(
|
||||||
|
traceback.format_exception_only(type(exc_val), exc_val)
|
||||||
|
)
|
||||||
|
formatted_exc = formatted_exc.rstrip() # remove trailing new line
|
||||||
|
if func in (os.unlink, os.remove, os.rmdir):
|
||||||
|
logger.debug(
|
||||||
|
"Failed to remove a temporary file '%s' due to %s.\n",
|
||||||
|
path,
|
||||||
|
formatted_exc,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.debug("%s failed with %s.", func.__qualname__, formatted_exc)
|
||||||
|
errors.append(exc_val)
|
||||||
|
|
||||||
|
if self.ignore_cleanup_errors:
|
||||||
|
try:
|
||||||
|
# first try with tenacity; retrying to handle ephemeral errors
|
||||||
|
rmtree(self._path, ignore_errors=False)
|
||||||
|
except OSError:
|
||||||
|
# last pass ignore/log all errors
|
||||||
|
rmtree(self._path, onexc=onerror)
|
||||||
|
if errors:
|
||||||
|
logger.warning(
|
||||||
|
"Failed to remove contents in a temporary directory '%s'.\n"
|
||||||
|
"You can safely remove it manually.",
|
||||||
|
self._path,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rmtree(self._path)
|
||||||
|
|
||||||
|
|
||||||
class AdjacentTempDirectory(TempDirectory):
|
class AdjacentTempDirectory(TempDirectory):
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Mercurial(VersionControl):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_base_rev_args(rev: str) -> List[str]:
|
def get_base_rev_args(rev: str) -> List[str]:
|
||||||
return [rev]
|
return ["-r", rev]
|
||||||
|
|
||||||
def fetch_new(
|
def fetch_new(
|
||||||
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
|
self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from .core import contents, where
|
from .core import contents, where
|
||||||
|
|
||||||
__all__ = ["contents", "where"]
|
__all__ = ["contents", "where"]
|
||||||
__version__ = "2022.12.07"
|
__version__ = "2023.07.22"
|
||||||
|
|
|
@ -791,34 +791,6 @@ uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
|
||||||
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
|
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post
|
|
||||||
# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post
|
|
||||||
# Label: "Hongkong Post Root CA 1"
|
|
||||||
# Serial: 1000
|
|
||||||
# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca
|
|
||||||
# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58
|
|
||||||
# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
|
|
||||||
FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
|
|
||||||
Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
|
|
||||||
A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
|
|
||||||
b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
|
||||||
AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
|
|
||||||
jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
|
|
||||||
PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
|
|
||||||
ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
|
|
||||||
nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
|
|
||||||
q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
|
|
||||||
MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
|
|
||||||
mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
|
|
||||||
7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
|
|
||||||
oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
|
|
||||||
EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
|
|
||||||
fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
|
|
||||||
AmvZWg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
|
# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
|
||||||
# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
|
# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
|
||||||
# Label: "SecureSign RootCA11"
|
# Label: "SecureSign RootCA11"
|
||||||
|
@ -1676,50 +1648,6 @@ HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx
|
||||||
SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
|
SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi
|
|
||||||
# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi
|
|
||||||
# Label: "E-Tugra Certification Authority"
|
|
||||||
# Serial: 7667447206703254355
|
|
||||||
# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49
|
|
||||||
# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39
|
|
||||||
# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
|
|
||||||
BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
|
|
||||||
aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
|
|
||||||
BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
|
|
||||||
Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
|
|
||||||
MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
|
|
||||||
BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
|
|
||||||
em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
|
|
||||||
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
|
|
||||||
B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
|
|
||||||
D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
|
|
||||||
Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
|
|
||||||
q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
|
|
||||||
k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
|
|
||||||
fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
|
|
||||||
dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
|
|
||||||
ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
|
|
||||||
zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
|
|
||||||
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
|
|
||||||
U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
|
|
||||||
Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
|
|
||||||
XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
|
|
||||||
Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
|
|
||||||
HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
|
|
||||||
GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
|
|
||||||
77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
|
|
||||||
+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
|
|
||||||
vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
|
|
||||||
FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
|
|
||||||
yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
|
|
||||||
AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
|
|
||||||
y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
|
|
||||||
NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
|
# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
|
||||||
# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
|
# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
|
||||||
# Label: "T-TeleSec GlobalRoot Class 2"
|
# Label: "T-TeleSec GlobalRoot Class 2"
|
||||||
|
@ -4397,73 +4325,6 @@ ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG
|
||||||
BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
|
BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
# Issuer: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
|
|
||||||
# Subject: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
|
|
||||||
# Label: "E-Tugra Global Root CA RSA v3"
|
|
||||||
# Serial: 75951268308633135324246244059508261641472512052
|
|
||||||
# MD5 Fingerprint: 22:be:10:f6:c2:f8:03:88:73:5f:33:29:47:28:47:a4
|
|
||||||
# SHA1 Fingerprint: e9:a8:5d:22:14:52:1c:5b:aa:0a:b4:be:24:6a:23:8a:c9:ba:e2:a9
|
|
||||||
# SHA256 Fingerprint: ef:66:b0:b1:0a:3c:db:9f:2e:36:48:c7:6b:d2:af:18:ea:d2:bf:e6:f1:17:65:5e:28:c4:06:0d:a1:a3:f4:c2
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
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
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Issuer: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
|
|
||||||
# Subject: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center
|
|
||||||
# Label: "E-Tugra Global Root CA ECC v3"
|
|
||||||
# Serial: 218504919822255052842371958738296604628416471745
|
|
||||||
# MD5 Fingerprint: 46:bc:81:bb:f1:b5:1e:f7:4b:96:bc:14:e2:e7:27:64
|
|
||||||
# SHA1 Fingerprint: 8a:2f:af:57:53:b1:b0:e6:a1:04:ec:5b:6a:69:71:6d:f6:1c:e2:84
|
|
||||||
# SHA256 Fingerprint: 87:3f:46:85:fa:7f:56:36:25:25:2e:6d:36:bc:d7:f1:6f:c2:49:51:f2:64:e4:7e:1b:95:4f:49:08:cd:ca:13
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw
|
|
||||||
gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn
|
|
||||||
cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD
|
|
||||||
VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2
|
|
||||||
NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r
|
|
||||||
YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh
|
|
||||||
IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF
|
|
||||||
Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ
|
|
||||||
KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK
|
|
||||||
fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB
|
|
||||||
Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C
|
|
||||||
MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp
|
|
||||||
ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6
|
|
||||||
7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx
|
|
||||||
vmjkI6TZraE3
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD.
|
# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD.
|
||||||
# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD.
|
# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD.
|
||||||
# Label: "Security Communication RootCA3"
|
# Label: "Security Communication RootCA3"
|
||||||
|
@ -4525,3 +4386,250 @@ BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu
|
||||||
9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O
|
9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O
|
||||||
be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=
|
be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
|
||||||
|
# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY
|
||||||
|
# Label: "BJCA Global Root CA1"
|
||||||
|
# Serial: 113562791157148395269083148143378328608
|
||||||
|
# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90
|
||||||
|
# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a
|
||||||
|
# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
r/6zcCwupvI=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
|
||||||
|
# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY
|
||||||
|
# Label: "BJCA Global Root CA2"
|
||||||
|
# Serial: 58605626836079930195615843123109055211
|
||||||
|
# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c
|
||||||
|
# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6
|
||||||
|
# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw
|
||||||
|
CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ
|
||||||
|
VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy
|
||||||
|
MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ
|
||||||
|
TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS
|
||||||
|
b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B
|
||||||
|
IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+
|
||||||
|
+kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK
|
||||||
|
sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
|
||||||
|
AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA
|
||||||
|
94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B
|
||||||
|
43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited
|
||||||
|
# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited
|
||||||
|
# Label: "Sectigo Public Server Authentication Root E46"
|
||||||
|
# Serial: 88989738453351742415770396670917916916
|
||||||
|
# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01
|
||||||
|
# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a
|
||||||
|
# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw
|
||||||
|
CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
|
||||||
|
ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN
|
||||||
|
MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG
|
||||||
|
A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT
|
||||||
|
ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||||
|
IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC
|
||||||
|
WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+
|
||||||
|
6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B
|
||||||
|
Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa
|
||||||
|
qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q
|
||||||
|
4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited
|
||||||
|
# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited
|
||||||
|
# Label: "Sectigo Public Server Authentication Root R46"
|
||||||
|
# Serial: 156256931880233212765902055439220583700
|
||||||
|
# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5
|
||||||
|
# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38
|
||||||
|
# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf
|
||||||
|
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
|
||||||
|
Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
|
||||||
|
HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY
|
||||||
|
MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp
|
||||||
|
YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa
|
||||||
|
ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz
|
||||||
|
SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf
|
||||||
|
iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X
|
||||||
|
ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3
|
||||||
|
IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS
|
||||||
|
VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE
|
||||||
|
SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu
|
||||||
|
+Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt
|
||||||
|
8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L
|
||||||
|
HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt
|
||||||
|
zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P
|
||||||
|
AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
|
||||||
|
mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ
|
||||||
|
YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52
|
||||||
|
gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA
|
||||||
|
Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB
|
||||||
|
JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX
|
||||||
|
DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui
|
||||||
|
TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5
|
||||||
|
dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65
|
||||||
|
LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp
|
||||||
|
0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY
|
||||||
|
QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation
|
||||||
|
# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation
|
||||||
|
# Label: "SSL.com TLS RSA Root CA 2022"
|
||||||
|
# Serial: 148535279242832292258835760425842727825
|
||||||
|
# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da
|
||||||
|
# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca
|
||||||
|
# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation
|
||||||
|
# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation
|
||||||
|
# Label: "SSL.com TLS ECC Root CA 2022"
|
||||||
|
# Serial: 26605119622390491762507526719404364228
|
||||||
|
# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5
|
||||||
|
# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39
|
||||||
|
# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw
|
||||||
|
CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT
|
||||||
|
U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2
|
||||||
|
MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh
|
||||||
|
dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG
|
||||||
|
ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm
|
||||||
|
acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN
|
||||||
|
SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME
|
||||||
|
GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW
|
||||||
|
uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp
|
||||||
|
15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN
|
||||||
|
b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos
|
||||||
|
# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos
|
||||||
|
# Label: "Atos TrustedRoot Root CA ECC TLS 2021"
|
||||||
|
# Serial: 81873346711060652204712539181482831616
|
||||||
|
# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8
|
||||||
|
# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd
|
||||||
|
# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w
|
||||||
|
LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w
|
||||||
|
CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0
|
||||||
|
MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF
|
||||||
|
Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI
|
||||||
|
zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X
|
||||||
|
tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4
|
||||||
|
AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2
|
||||||
|
KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD
|
||||||
|
aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu
|
||||||
|
CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo
|
||||||
|
9H1/IISpQuQo
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos
|
||||||
|
# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos
|
||||||
|
# Label: "Atos TrustedRoot Root CA RSA TLS 2021"
|
||||||
|
# Serial: 111436099570196163832749341232207667876
|
||||||
|
# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2
|
||||||
|
# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48
|
||||||
|
# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
Copyright Jason R. Coombs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to
|
of this software and associated documentation files (the "Software"), to
|
||||||
deal in the Software without restriction, including without limitation the
|
deal in the Software without restriction, including without limitation the
|
||||||
|
|
|
@ -13,11 +13,8 @@ The package resource API is designed to work with normal filesystem packages,
|
||||||
.zip files and with custom PEP 302 loaders that support the ``get_data()``
|
.zip files and with custom PEP 302 loaders that support the ``get_data()``
|
||||||
method.
|
method.
|
||||||
|
|
||||||
This module is deprecated. Users are directed to
|
This module is deprecated. Users are directed to :mod:`importlib.resources`,
|
||||||
`importlib.resources <https://docs.python.org/3/library/importlib.resources.html>`_
|
:mod:`importlib.metadata` and :pypi:`packaging` instead.
|
||||||
and
|
|
||||||
`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
|
|
||||||
instead.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -118,7 +115,12 @@ _namespace_handlers = None
|
||||||
_namespace_packages = None
|
_namespace_packages = None
|
||||||
|
|
||||||
|
|
||||||
warnings.warn("pkg_resources is deprecated as an API", DeprecationWarning)
|
warnings.warn(
|
||||||
|
"pkg_resources is deprecated as an API. "
|
||||||
|
"See https://setuptools.pypa.io/en/latest/pkg_resources.html",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
_PEP440_FALLBACK = re.compile(r"^v?(?P<safe>(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I)
|
_PEP440_FALLBACK = re.compile(r"^v?(?P<safe>(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I)
|
||||||
|
@ -1659,10 +1661,9 @@ is not allowed.
|
||||||
|
|
||||||
# for compatibility, warn; in future
|
# for compatibility, warn; in future
|
||||||
# raise ValueError(msg)
|
# raise ValueError(msg)
|
||||||
warnings.warn(
|
issue_warning(
|
||||||
msg[:-1] + " and will raise exceptions in a future release.",
|
msg[:-1] + " and will raise exceptions in a future release.",
|
||||||
DeprecationWarning,
|
DeprecationWarning,
|
||||||
stacklevel=4,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get(self, path):
|
def _get(self, path):
|
||||||
|
|
|
@ -6,17 +6,20 @@ from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if sys.version_info >= (3, 8): # pragma: no cover (py38+)
|
|
||||||
from typing import Literal
|
|
||||||
else: # pragma: no cover (py38+)
|
|
||||||
from pip._vendor.typing_extensions import Literal
|
|
||||||
|
|
||||||
from .api import PlatformDirsABC
|
from .api import PlatformDirsABC
|
||||||
from .version import __version__
|
from .version import __version__
|
||||||
from .version import __version_tuple__ as __version_info__
|
from .version import __version_tuple__ as __version_info__
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 8): # pragma: no cover (py38+)
|
||||||
|
from typing import Literal
|
||||||
|
else: # pragma: no cover (py38+)
|
||||||
|
from pip._vendor.typing_extensions import Literal
|
||||||
|
|
||||||
|
|
||||||
def _set_platform_dir_class() -> type[PlatformDirsABC]:
|
def _set_platform_dir_class() -> type[PlatformDirsABC]:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
|
@ -48,8 +51,8 @@ def user_data_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -72,8 +75,8 @@ def site_data_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
multipath: bool = False,
|
multipath: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -96,8 +99,8 @@ def user_config_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -120,8 +123,8 @@ def site_config_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
multipath: bool = False,
|
multipath: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -144,8 +147,8 @@ def user_cache_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -168,8 +171,8 @@ def site_cache_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -192,8 +195,8 @@ def user_state_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -216,8 +219,8 @@ def user_log_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -237,18 +240,36 @@ def user_log_dir(
|
||||||
|
|
||||||
|
|
||||||
def user_documents_dir() -> str:
|
def user_documents_dir() -> str:
|
||||||
"""
|
""":returns: documents directory tied to the user"""
|
||||||
:returns: documents directory tied to the user
|
|
||||||
"""
|
|
||||||
return PlatformDirs().user_documents_dir
|
return PlatformDirs().user_documents_dir
|
||||||
|
|
||||||
|
|
||||||
|
def user_downloads_dir() -> str:
|
||||||
|
""":returns: downloads directory tied to the user"""
|
||||||
|
return PlatformDirs().user_downloads_dir
|
||||||
|
|
||||||
|
|
||||||
|
def user_pictures_dir() -> str:
|
||||||
|
""":returns: pictures directory tied to the user"""
|
||||||
|
return PlatformDirs().user_pictures_dir
|
||||||
|
|
||||||
|
|
||||||
|
def user_videos_dir() -> str:
|
||||||
|
""":returns: videos directory tied to the user"""
|
||||||
|
return PlatformDirs().user_videos_dir
|
||||||
|
|
||||||
|
|
||||||
|
def user_music_dir() -> str:
|
||||||
|
""":returns: music directory tied to the user"""
|
||||||
|
return PlatformDirs().user_music_dir
|
||||||
|
|
||||||
|
|
||||||
def user_runtime_dir(
|
def user_runtime_dir(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -271,8 +292,8 @@ def user_data_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -295,8 +316,8 @@ def site_data_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
multipath: bool = False,
|
multipath: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -319,8 +340,8 @@ def user_config_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -343,8 +364,8 @@ def site_config_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
multipath: bool = False,
|
multipath: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -367,8 +388,8 @@ def site_cache_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -391,8 +412,8 @@ def user_cache_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -415,8 +436,8 @@ def user_state_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -439,8 +460,8 @@ def user_log_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -460,18 +481,36 @@ def user_log_path(
|
||||||
|
|
||||||
|
|
||||||
def user_documents_path() -> Path:
|
def user_documents_path() -> Path:
|
||||||
"""
|
""":returns: documents path tied to the user"""
|
||||||
:returns: documents path tied to the user
|
|
||||||
"""
|
|
||||||
return PlatformDirs().user_documents_path
|
return PlatformDirs().user_documents_path
|
||||||
|
|
||||||
|
|
||||||
|
def user_downloads_path() -> Path:
|
||||||
|
""":returns: downloads path tied to the user"""
|
||||||
|
return PlatformDirs().user_downloads_path
|
||||||
|
|
||||||
|
|
||||||
|
def user_pictures_path() -> Path:
|
||||||
|
""":returns: pictures path tied to the user"""
|
||||||
|
return PlatformDirs().user_pictures_path
|
||||||
|
|
||||||
|
|
||||||
|
def user_videos_path() -> Path:
|
||||||
|
""":returns: videos path tied to the user"""
|
||||||
|
return PlatformDirs().user_videos_path
|
||||||
|
|
||||||
|
|
||||||
|
def user_music_path() -> Path:
|
||||||
|
""":returns: music path tied to the user"""
|
||||||
|
return PlatformDirs().user_music_path
|
||||||
|
|
||||||
|
|
||||||
def user_runtime_path(
|
def user_runtime_path(
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
) -> Path:
|
) -> Path:
|
||||||
"""
|
"""
|
||||||
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
:param appname: See `appname <platformdirs.api.PlatformDirsABC.appname>`.
|
||||||
|
@ -502,6 +541,10 @@ __all__ = [
|
||||||
"user_state_dir",
|
"user_state_dir",
|
||||||
"user_log_dir",
|
"user_log_dir",
|
||||||
"user_documents_dir",
|
"user_documents_dir",
|
||||||
|
"user_downloads_dir",
|
||||||
|
"user_pictures_dir",
|
||||||
|
"user_videos_dir",
|
||||||
|
"user_music_dir",
|
||||||
"user_runtime_dir",
|
"user_runtime_dir",
|
||||||
"site_data_dir",
|
"site_data_dir",
|
||||||
"site_config_dir",
|
"site_config_dir",
|
||||||
|
@ -512,6 +555,10 @@ __all__ = [
|
||||||
"user_state_path",
|
"user_state_path",
|
||||||
"user_log_path",
|
"user_log_path",
|
||||||
"user_documents_path",
|
"user_documents_path",
|
||||||
|
"user_downloads_path",
|
||||||
|
"user_pictures_path",
|
||||||
|
"user_videos_path",
|
||||||
|
"user_music_path",
|
||||||
"user_runtime_path",
|
"user_runtime_path",
|
||||||
"site_data_path",
|
"site_data_path",
|
||||||
"site_config_path",
|
"site_config_path",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""Main entry point."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pip._vendor.platformdirs import PlatformDirs, __version__
|
from pip._vendor.platformdirs import PlatformDirs, __version__
|
||||||
|
@ -9,6 +10,10 @@ PROPS = (
|
||||||
"user_state_dir",
|
"user_state_dir",
|
||||||
"user_log_dir",
|
"user_log_dir",
|
||||||
"user_documents_dir",
|
"user_documents_dir",
|
||||||
|
"user_downloads_dir",
|
||||||
|
"user_pictures_dir",
|
||||||
|
"user_videos_dir",
|
||||||
|
"user_music_dir",
|
||||||
"user_runtime_dir",
|
"user_runtime_dir",
|
||||||
"site_data_dir",
|
"site_data_dir",
|
||||||
"site_config_dir",
|
"site_config_dir",
|
||||||
|
@ -17,30 +22,31 @@ PROPS = (
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
"""Run main entry point."""
|
||||||
app_name = "MyApp"
|
app_name = "MyApp"
|
||||||
app_author = "MyCompany"
|
app_author = "MyCompany"
|
||||||
|
|
||||||
print(f"-- platformdirs {__version__} --")
|
print(f"-- platformdirs {__version__} --") # noqa: T201
|
||||||
|
|
||||||
print("-- app dirs (with optional 'version')")
|
print("-- app dirs (with optional 'version')") # noqa: T201
|
||||||
dirs = PlatformDirs(app_name, app_author, version="1.0")
|
dirs = PlatformDirs(app_name, app_author, version="1.0")
|
||||||
for prop in PROPS:
|
for prop in PROPS:
|
||||||
print(f"{prop}: {getattr(dirs, prop)}")
|
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||||
|
|
||||||
print("\n-- app dirs (without optional 'version')")
|
print("\n-- app dirs (without optional 'version')") # noqa: T201
|
||||||
dirs = PlatformDirs(app_name, app_author)
|
dirs = PlatformDirs(app_name, app_author)
|
||||||
for prop in PROPS:
|
for prop in PROPS:
|
||||||
print(f"{prop}: {getattr(dirs, prop)}")
|
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||||
|
|
||||||
print("\n-- app dirs (without optional 'appauthor')")
|
print("\n-- app dirs (without optional 'appauthor')") # noqa: T201
|
||||||
dirs = PlatformDirs(app_name)
|
dirs = PlatformDirs(app_name)
|
||||||
for prop in PROPS:
|
for prop in PROPS:
|
||||||
print(f"{prop}: {getattr(dirs, prop)}")
|
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||||
|
|
||||||
print("\n-- app dirs (with disabled 'appauthor')")
|
print("\n-- app dirs (with disabled 'appauthor')") # noqa: T201
|
||||||
dirs = PlatformDirs(app_name, appauthor=False)
|
dirs = PlatformDirs(app_name, appauthor=False)
|
||||||
for prop in PROPS:
|
for prop in PROPS:
|
||||||
print(f"{prop}: {getattr(dirs, prop)}")
|
print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""Android."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -30,7 +31,8 @@ class Android(PlatformDirsABC):
|
||||||
@property
|
@property
|
||||||
def user_config_dir(self) -> str:
|
def user_config_dir(self) -> str:
|
||||||
"""
|
"""
|
||||||
:return: config directory tied to the user, e.g. ``/data/user/<userid>/<packagename>/shared_prefs/<AppName>``
|
:return: config directory tied to the user, e.g. \
|
||||||
|
``/data/user/<userid>/<packagename>/shared_prefs/<AppName>``
|
||||||
"""
|
"""
|
||||||
return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs")
|
return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs")
|
||||||
|
|
||||||
|
@ -62,16 +64,34 @@ class Android(PlatformDirsABC):
|
||||||
"""
|
"""
|
||||||
path = self.user_cache_dir
|
path = self.user_cache_dir
|
||||||
if self.opinion:
|
if self.opinion:
|
||||||
path = os.path.join(path, "log")
|
path = os.path.join(path, "log") # noqa: PTH118
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_documents_dir(self) -> str:
|
def user_documents_dir(self) -> str:
|
||||||
"""
|
""":return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``"""
|
||||||
:return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``
|
|
||||||
"""
|
|
||||||
return _android_documents_folder()
|
return _android_documents_folder()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_downloads_dir(self) -> str:
|
||||||
|
""":return: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``"""
|
||||||
|
return _android_downloads_folder()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_pictures_dir(self) -> str:
|
||||||
|
""":return: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``"""
|
||||||
|
return _android_pictures_folder()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_videos_dir(self) -> str:
|
||||||
|
""":return: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``"""
|
||||||
|
return _android_videos_folder()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_music_dir(self) -> str:
|
||||||
|
""":return: music directory tied to the user e.g. ``/storage/emulated/0/Music``"""
|
||||||
|
return _android_music_folder()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_runtime_dir(self) -> str:
|
def user_runtime_dir(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -80,20 +100,20 @@ class Android(PlatformDirsABC):
|
||||||
"""
|
"""
|
||||||
path = self.user_cache_dir
|
path = self.user_cache_dir
|
||||||
if self.opinion:
|
if self.opinion:
|
||||||
path = os.path.join(path, "tmp")
|
path = os.path.join(path, "tmp") # noqa: PTH118
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=1)
|
@lru_cache(maxsize=1)
|
||||||
def _android_folder() -> str | None:
|
def _android_folder() -> str | None:
|
||||||
""":return: base folder for the Android OS or None if cannot be found"""
|
""":return: base folder for the Android OS or None if it cannot be found"""
|
||||||
try:
|
try:
|
||||||
# First try to get path to android app via pyjnius
|
# First try to get path to android app via pyjnius
|
||||||
from jnius import autoclass
|
from jnius import autoclass
|
||||||
|
|
||||||
Context = autoclass("android.content.Context") # noqa: N806
|
context = autoclass("android.content.Context")
|
||||||
result: str | None = Context.getFilesDir().getParentFile().getAbsolutePath()
|
result: str | None = context.getFilesDir().getParentFile().getAbsolutePath()
|
||||||
except Exception:
|
except Exception: # noqa: BLE001
|
||||||
# if fails find an android folder looking path on the sys.path
|
# if fails find an android folder looking path on the sys.path
|
||||||
pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
|
pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
|
||||||
for path in sys.path:
|
for path in sys.path:
|
||||||
|
@ -112,15 +132,79 @@ def _android_documents_folder() -> str:
|
||||||
try:
|
try:
|
||||||
from jnius import autoclass
|
from jnius import autoclass
|
||||||
|
|
||||||
Context = autoclass("android.content.Context") # noqa: N806
|
context = autoclass("android.content.Context")
|
||||||
Environment = autoclass("android.os.Environment") # noqa: N806
|
environment = autoclass("android.os.Environment")
|
||||||
documents_dir: str = Context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
|
documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
|
||||||
except Exception:
|
except Exception: # noqa: BLE001
|
||||||
documents_dir = "/storage/emulated/0/Documents"
|
documents_dir = "/storage/emulated/0/Documents"
|
||||||
|
|
||||||
return documents_dir
|
return documents_dir
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _android_downloads_folder() -> str:
|
||||||
|
""":return: downloads folder for the Android OS"""
|
||||||
|
# Get directories with pyjnius
|
||||||
|
try:
|
||||||
|
from jnius import autoclass
|
||||||
|
|
||||||
|
context = autoclass("android.content.Context")
|
||||||
|
environment = autoclass("android.os.Environment")
|
||||||
|
downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
downloads_dir = "/storage/emulated/0/Downloads"
|
||||||
|
|
||||||
|
return downloads_dir
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _android_pictures_folder() -> str:
|
||||||
|
""":return: pictures folder for the Android OS"""
|
||||||
|
# Get directories with pyjnius
|
||||||
|
try:
|
||||||
|
from jnius import autoclass
|
||||||
|
|
||||||
|
context = autoclass("android.content.Context")
|
||||||
|
environment = autoclass("android.os.Environment")
|
||||||
|
pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath()
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
pictures_dir = "/storage/emulated/0/Pictures"
|
||||||
|
|
||||||
|
return pictures_dir
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _android_videos_folder() -> str:
|
||||||
|
""":return: videos folder for the Android OS"""
|
||||||
|
# Get directories with pyjnius
|
||||||
|
try:
|
||||||
|
from jnius import autoclass
|
||||||
|
|
||||||
|
context = autoclass("android.content.Context")
|
||||||
|
environment = autoclass("android.os.Environment")
|
||||||
|
videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath()
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
videos_dir = "/storage/emulated/0/DCIM/Camera"
|
||||||
|
|
||||||
|
return videos_dir
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _android_music_folder() -> str:
|
||||||
|
""":return: music folder for the Android OS"""
|
||||||
|
# Get directories with pyjnius
|
||||||
|
try:
|
||||||
|
from jnius import autoclass
|
||||||
|
|
||||||
|
context = autoclass("android.content.Context")
|
||||||
|
environment = autoclass("android.os.Environment")
|
||||||
|
music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath()
|
||||||
|
except Exception: # noqa: BLE001
|
||||||
|
music_dir = "/storage/emulated/0/Music"
|
||||||
|
|
||||||
|
return music_dir
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Android",
|
"Android",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,29 +1,33 @@
|
||||||
|
"""Base API."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if sys.version_info >= (3, 8): # pragma: no branch
|
if TYPE_CHECKING:
|
||||||
from typing import Literal # pragma: no cover
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 8): # pragma: no cover (py38+)
|
||||||
|
from typing import Literal
|
||||||
|
else: # pragma: no cover (py38+)
|
||||||
|
from pip._vendor.typing_extensions import Literal
|
||||||
|
|
||||||
|
|
||||||
class PlatformDirsABC(ABC):
|
class PlatformDirsABC(ABC):
|
||||||
"""
|
"""Abstract base class for platform directories."""
|
||||||
Abstract base class for platform directories.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
def __init__( # noqa: PLR0913
|
||||||
self,
|
self,
|
||||||
appname: str | None = None,
|
appname: str | None = None,
|
||||||
appauthor: str | None | Literal[False] = None,
|
appauthor: str | None | Literal[False] = None,
|
||||||
version: str | None = None,
|
version: str | None = None,
|
||||||
roaming: bool = False,
|
roaming: bool = False, # noqa: FBT001, FBT002
|
||||||
multipath: bool = False,
|
multipath: bool = False, # noqa: FBT001, FBT002
|
||||||
opinion: bool = True,
|
opinion: bool = True, # noqa: FBT001, FBT002
|
||||||
ensure_exists: bool = False,
|
ensure_exists: bool = False, # noqa: FBT001, FBT002
|
||||||
):
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create a new platform directory.
|
Create a new platform directory.
|
||||||
|
|
||||||
|
@ -70,7 +74,7 @@ class PlatformDirsABC(ABC):
|
||||||
params.append(self.appname)
|
params.append(self.appname)
|
||||||
if self.version:
|
if self.version:
|
||||||
params.append(self.version)
|
params.append(self.version)
|
||||||
path = os.path.join(base[0], *params)
|
path = os.path.join(base[0], *params) # noqa: PTH118
|
||||||
self._optionally_create_directory(path)
|
self._optionally_create_directory(path)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@ -123,6 +127,26 @@ class PlatformDirsABC(ABC):
|
||||||
def user_documents_dir(self) -> str:
|
def user_documents_dir(self) -> str:
|
||||||
""":return: documents directory tied to the user"""
|
""":return: documents directory tied to the user"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def user_downloads_dir(self) -> str:
|
||||||
|
""":return: downloads directory tied to the user"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def user_pictures_dir(self) -> str:
|
||||||
|
""":return: pictures directory tied to the user"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def user_videos_dir(self) -> str:
|
||||||
|
""":return: videos directory tied to the user"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def user_music_dir(self) -> str:
|
||||||
|
""":return: music directory tied to the user"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def user_runtime_dir(self) -> str:
|
def user_runtime_dir(self) -> str:
|
||||||
|
@ -173,6 +197,26 @@ class PlatformDirsABC(ABC):
|
||||||
""":return: documents path tied to the user"""
|
""":return: documents path tied to the user"""
|
||||||
return Path(self.user_documents_dir)
|
return Path(self.user_documents_dir)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_downloads_path(self) -> Path:
|
||||||
|
""":return: downloads path tied to the user"""
|
||||||
|
return Path(self.user_downloads_dir)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_pictures_path(self) -> Path:
|
||||||
|
""":return: pictures path tied to the user"""
|
||||||
|
return Path(self.user_pictures_dir)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_videos_path(self) -> Path:
|
||||||
|
""":return: videos path tied to the user"""
|
||||||
|
return Path(self.user_videos_dir)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_music_path(self) -> Path:
|
||||||
|
""":return: music path tied to the user"""
|
||||||
|
return Path(self.user_music_dir)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_runtime_path(self) -> Path:
|
def user_runtime_path(self) -> Path:
|
||||||
""":return: runtime path tied to the user"""
|
""":return: runtime path tied to the user"""
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
"""macOS."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os.path
|
||||||
|
|
||||||
from .api import PlatformDirsABC
|
from .api import PlatformDirsABC
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ class MacOS(PlatformDirsABC):
|
||||||
@property
|
@property
|
||||||
def user_data_dir(self) -> str:
|
def user_data_dir(self) -> str:
|
||||||
""":return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
|
""":return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
|
||||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support"))
|
return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def site_data_dir(self) -> str:
|
def site_data_dir(self) -> str:
|
||||||
|
@ -37,7 +38,7 @@ class MacOS(PlatformDirsABC):
|
||||||
@property
|
@property
|
||||||
def user_cache_dir(self) -> str:
|
def user_cache_dir(self) -> str:
|
||||||
""":return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
|
""":return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
|
||||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))
|
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches")) # noqa: PTH111
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def site_cache_dir(self) -> str:
|
def site_cache_dir(self) -> str:
|
||||||
|
@ -52,17 +53,37 @@ class MacOS(PlatformDirsABC):
|
||||||
@property
|
@property
|
||||||
def user_log_dir(self) -> str:
|
def user_log_dir(self) -> str:
|
||||||
""":return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
|
""":return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
|
||||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs"))
|
return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) # noqa: PTH111
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_documents_dir(self) -> str:
|
def user_documents_dir(self) -> str:
|
||||||
""":return: documents directory tied to the user, e.g. ``~/Documents``"""
|
""":return: documents directory tied to the user, e.g. ``~/Documents``"""
|
||||||
return os.path.expanduser("~/Documents")
|
return os.path.expanduser("~/Documents") # noqa: PTH111
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_downloads_dir(self) -> str:
|
||||||
|
""":return: downloads directory tied to the user, e.g. ``~/Downloads``"""
|
||||||
|
return os.path.expanduser("~/Downloads") # noqa: PTH111
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_pictures_dir(self) -> str:
|
||||||
|
""":return: pictures directory tied to the user, e.g. ``~/Pictures``"""
|
||||||
|
return os.path.expanduser("~/Pictures") # noqa: PTH111
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_videos_dir(self) -> str:
|
||||||
|
""":return: videos directory tied to the user, e.g. ``~/Movies``"""
|
||||||
|
return os.path.expanduser("~/Movies") # noqa: PTH111
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_music_dir(self) -> str:
|
||||||
|
""":return: music directory tied to the user, e.g. ``~/Music``"""
|
||||||
|
return os.path.expanduser("~/Music") # noqa: PTH111
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_runtime_dir(self) -> str:
|
def user_runtime_dir(self) -> str:
|
||||||
""":return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
|
""":return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
|
||||||
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems"))
|
return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) # noqa: PTH111
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
"""Unix."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -7,12 +8,14 @@ from pathlib import Path
|
||||||
|
|
||||||
from .api import PlatformDirsABC
|
from .api import PlatformDirsABC
|
||||||
|
|
||||||
if sys.platform.startswith("linux"): # pragma: no branch # no op check, only to please the type checker
|
if sys.platform == "win32":
|
||||||
from os import getuid
|
|
||||||
else:
|
|
||||||
|
|
||||||
def getuid() -> int:
|
def getuid() -> int:
|
||||||
raise RuntimeError("should only be used on Linux")
|
msg = "should only be used on Unix"
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
else:
|
||||||
|
from os import getuid
|
||||||
|
|
||||||
|
|
||||||
class Unix(PlatformDirsABC):
|
class Unix(PlatformDirsABC):
|
||||||
|
@ -36,7 +39,7 @@ class Unix(PlatformDirsABC):
|
||||||
"""
|
"""
|
||||||
path = os.environ.get("XDG_DATA_HOME", "")
|
path = os.environ.get("XDG_DATA_HOME", "")
|
||||||
if not path.strip():
|
if not path.strip():
|
||||||
path = os.path.expanduser("~/.local/share")
|
path = os.path.expanduser("~/.local/share") # noqa: PTH111
|
||||||
return self._append_app_name_and_version(path)
|
return self._append_app_name_and_version(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -56,7 +59,7 @@ class Unix(PlatformDirsABC):
|
||||||
path_list = path.split(os.pathsep)
|
path_list = path.split(os.pathsep)
|
||||||
if not self.multipath:
|
if not self.multipath:
|
||||||
path_list = path_list[0:1]
|
path_list = path_list[0:1]
|
||||||
path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list]
|
path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list] # noqa: PTH111
|
||||||
return os.pathsep.join(path_list)
|
return os.pathsep.join(path_list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -67,7 +70,7 @@ class Unix(PlatformDirsABC):
|
||||||
"""
|
"""
|
||||||
path = os.environ.get("XDG_CONFIG_HOME", "")
|
path = os.environ.get("XDG_CONFIG_HOME", "")
|
||||||
if not path.strip():
|
if not path.strip():
|
||||||
path = os.path.expanduser("~/.config")
|
path = os.path.expanduser("~/.config") # noqa: PTH111
|
||||||
return self._append_app_name_and_version(path)
|
return self._append_app_name_and_version(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -91,15 +94,13 @@ class Unix(PlatformDirsABC):
|
||||||
"""
|
"""
|
||||||
path = os.environ.get("XDG_CACHE_HOME", "")
|
path = os.environ.get("XDG_CACHE_HOME", "")
|
||||||
if not path.strip():
|
if not path.strip():
|
||||||
path = os.path.expanduser("~/.cache")
|
path = os.path.expanduser("~/.cache") # noqa: PTH111
|
||||||
return self._append_app_name_and_version(path)
|
return self._append_app_name_and_version(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def site_cache_dir(self) -> str:
|
def site_cache_dir(self) -> str:
|
||||||
"""
|
""":return: cache directory shared by users, e.g. ``/var/tmp/$appname/$version``"""
|
||||||
:return: cache directory shared by users, e.g. ``/var/tmp/$appname/$version``
|
return self._append_app_name_and_version("/var/tmp") # noqa: S108
|
||||||
"""
|
|
||||||
return self._append_app_name_and_version("/var/tmp")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_state_dir(self) -> str:
|
def user_state_dir(self) -> str:
|
||||||
|
@ -109,41 +110,60 @@ class Unix(PlatformDirsABC):
|
||||||
"""
|
"""
|
||||||
path = os.environ.get("XDG_STATE_HOME", "")
|
path = os.environ.get("XDG_STATE_HOME", "")
|
||||||
if not path.strip():
|
if not path.strip():
|
||||||
path = os.path.expanduser("~/.local/state")
|
path = os.path.expanduser("~/.local/state") # noqa: PTH111
|
||||||
return self._append_app_name_and_version(path)
|
return self._append_app_name_and_version(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_log_dir(self) -> str:
|
def user_log_dir(self) -> str:
|
||||||
"""
|
""":return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it"""
|
||||||
:return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it
|
|
||||||
"""
|
|
||||||
path = self.user_state_dir
|
path = self.user_state_dir
|
||||||
if self.opinion:
|
if self.opinion:
|
||||||
path = os.path.join(path, "log")
|
path = os.path.join(path, "log") # noqa: PTH118
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_documents_dir(self) -> str:
|
def user_documents_dir(self) -> str:
|
||||||
"""
|
""":return: documents directory tied to the user, e.g. ``~/Documents``"""
|
||||||
:return: documents directory tied to the user, e.g. ``~/Documents``
|
return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents")
|
||||||
"""
|
|
||||||
documents_dir = _get_user_dirs_folder("XDG_DOCUMENTS_DIR")
|
|
||||||
if documents_dir is None:
|
|
||||||
documents_dir = os.environ.get("XDG_DOCUMENTS_DIR", "").strip()
|
|
||||||
if not documents_dir:
|
|
||||||
documents_dir = os.path.expanduser("~/Documents")
|
|
||||||
|
|
||||||
return documents_dir
|
@property
|
||||||
|
def user_downloads_dir(self) -> str:
|
||||||
|
""":return: downloads directory tied to the user, e.g. ``~/Downloads``"""
|
||||||
|
return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_pictures_dir(self) -> str:
|
||||||
|
""":return: pictures directory tied to the user, e.g. ``~/Pictures``"""
|
||||||
|
return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_videos_dir(self) -> str:
|
||||||
|
""":return: videos directory tied to the user, e.g. ``~/Videos``"""
|
||||||
|
return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_music_dir(self) -> str:
|
||||||
|
""":return: music directory tied to the user, e.g. ``~/Music``"""
|
||||||
|
return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def user_runtime_dir(self) -> str:
|
def user_runtime_dir(self) -> str:
|
||||||
"""
|
"""
|
||||||
:return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or
|
:return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or
|
||||||
``$XDG_RUNTIME_DIR/$appname/$version``
|
``$XDG_RUNTIME_DIR/$appname/$version``.
|
||||||
|
|
||||||
|
For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if
|
||||||
|
exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR``
|
||||||
|
is not set.
|
||||||
"""
|
"""
|
||||||
path = os.environ.get("XDG_RUNTIME_DIR", "")
|
path = os.environ.get("XDG_RUNTIME_DIR", "")
|
||||||
if not path.strip():
|
if not path.strip():
|
||||||
path = f"/run/user/{getuid()}"
|
if sys.platform.startswith(("freebsd", "openbsd", "netbsd")):
|
||||||
|
path = f"/var/run/user/{getuid()}"
|
||||||
|
if not Path(path).exists():
|
||||||
|
path = f"/tmp/runtime-{getuid()}" # noqa: S108
|
||||||
|
else:
|
||||||
|
path = f"/run/user/{getuid()}"
|
||||||
return self._append_app_name_and_version(path)
|
return self._append_app_name_and_version(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -168,13 +188,23 @@ class Unix(PlatformDirsABC):
|
||||||
return Path(directory)
|
return Path(directory)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str:
|
||||||
|
media_dir = _get_user_dirs_folder(env_var)
|
||||||
|
if media_dir is None:
|
||||||
|
media_dir = os.environ.get(env_var, "").strip()
|
||||||
|
if not media_dir:
|
||||||
|
media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111
|
||||||
|
|
||||||
|
return media_dir
|
||||||
|
|
||||||
|
|
||||||
def _get_user_dirs_folder(key: str) -> str | None:
|
def _get_user_dirs_folder(key: str) -> str | None:
|
||||||
"""Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/"""
|
"""Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/."""
|
||||||
user_dirs_config_path = os.path.join(Unix().user_config_dir, "user-dirs.dirs")
|
user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs"
|
||||||
if os.path.exists(user_dirs_config_path):
|
if user_dirs_config_path.exists():
|
||||||
parser = ConfigParser()
|
parser = ConfigParser()
|
||||||
|
|
||||||
with open(user_dirs_config_path) as stream:
|
with user_dirs_config_path.open() as stream:
|
||||||
# Add fake section header, so ConfigParser doesn't complain
|
# Add fake section header, so ConfigParser doesn't complain
|
||||||
parser.read_string(f"[top]\n{stream.read()}")
|
parser.read_string(f"[top]\n{stream.read()}")
|
||||||
|
|
||||||
|
@ -183,8 +213,7 @@ def _get_user_dirs_folder(key: str) -> str | None:
|
||||||
|
|
||||||
path = parser["top"][key].strip('"')
|
path = parser["top"][key].strip('"')
|
||||||
# Handle relative home paths
|
# Handle relative home paths
|
||||||
path = path.replace("$HOME", os.path.expanduser("~"))
|
return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111
|
||||||
return path
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# file generated by setuptools_scm
|
# file generated by setuptools_scm
|
||||||
# don't change, don't track in version control
|
# don't change, don't track in version control
|
||||||
__version__ = version = '3.2.0'
|
__version__ = version = '3.8.1'
|
||||||
__version_tuple__ = version_tuple = (3, 2, 0)
|
__version_tuple__ = version_tuple = (3, 8, 1)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue