Skip to content

Commit 219dc66

Browse files
committed
Merge branch 'main' into release/22.1.2
2 parents 03a27aa + 7be9b42 commit 219dc66

File tree

12 files changed

+198
-21
lines changed

12 files changed

+198
-21
lines changed

docs/html/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ lists or chat rooms:
4848
[packaging-discourse]: https://discuss.python.org/c/packaging/14
4949
[irc-pypa]: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
5050
[irc-pypa-dev]: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
51+
52+
If you find any security issues, please report to [[email protected]](mailto:[email protected])

docs/html/topics/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,9 @@ Use `no`, `false` or `0` instead.
213213

214214
## Precedence / Override order
215215

216-
Command line options have override environment variables, which override the
216+
Command line options override environment variables, which override the
217217
values in a configuration file. Within the configuration file, values in
218-
command-specific sections over values in the global section.
218+
command-specific sections override values in the global section.
219219

220220
Examples:
221221

news/10979.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Revert `#10979 <https://github.com/pypa/pip/issues/10979>`_ since it introduced a regression in certain edge cases.

news/11082.feature.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add support to use `truststore <https://pypi.org/project/truststore/>`_ as an alternative SSL certificate verification backend. The backend can be enabled on Python 3.10 and later by installing ``truststore`` into the environment, and adding the ``--use-feature=truststore`` flag to various pip commands.
2+
3+
``truststore`` differs from the current default verification backend (provided by ``certifi``) in it uses the operating system’s trust store, which can be better controlled and augmented to better support non-standard certificates. Depending on feedback, pip may switch to this as the default certificate verification backend in the future.

news/11099.process.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove reliance on the stdlib cgi module, which is deprecated in Python 3.11.

news/11136.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix an incorrect assertion in the logging logic, that prevented the upgrade prompt from being presented.

src/pip/_internal/cli/cmdoptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,7 @@ def check_list_path_option(options: Values) -> None:
10001000
metavar="feature",
10011001
action="append",
10021002
default=[],
1003-
choices=["2020-resolver", "fast-deps"],
1003+
choices=["2020-resolver", "fast-deps", "truststore"],
10041004
help="Enable new functionality, that may be backward incompatible.",
10051005
)
10061006

src/pip/_internal/cli/req_command.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import sys
1111
from functools import partial
1212
from optparse import Values
13-
from typing import Any, List, Optional, Tuple
13+
from typing import TYPE_CHECKING, Any, List, Optional, Tuple
1414

1515
from pip._internal.cache import WheelCache
1616
from pip._internal.cli import cmdoptions
@@ -42,9 +42,33 @@
4242
)
4343
from pip._internal.utils.virtualenv import running_under_virtualenv
4444

45+
if TYPE_CHECKING:
46+
from ssl import SSLContext
47+
4548
logger = logging.getLogger(__name__)
4649

4750

51+
def _create_truststore_ssl_context() -> Optional["SSLContext"]:
52+
if sys.version_info < (3, 10):
53+
raise CommandError("The truststore feature is only available for Python 3.10+")
54+
55+
try:
56+
import ssl
57+
except ImportError:
58+
logger.warning("Disabling truststore since ssl support is missing")
59+
return None
60+
61+
try:
62+
import truststore
63+
except ImportError:
64+
raise CommandError(
65+
"To use the truststore feature, 'truststore' must be installed into "
66+
"pip's current environment."
67+
)
68+
69+
return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
70+
71+
4872
class SessionCommandMixin(CommandContextMixIn):
4973

5074
"""
@@ -84,15 +108,27 @@ def _build_session(
84108
options: Values,
85109
retries: Optional[int] = None,
86110
timeout: Optional[int] = None,
111+
fallback_to_certifi: bool = False,
87112
) -> PipSession:
88-
assert not options.cache_dir or os.path.isabs(options.cache_dir)
113+
cache_dir = options.cache_dir
114+
assert not cache_dir or os.path.isabs(cache_dir)
115+
116+
if "truststore" in options.features_enabled:
117+
try:
118+
ssl_context = _create_truststore_ssl_context()
119+
except Exception:
120+
if not fallback_to_certifi:
121+
raise
122+
ssl_context = None
123+
else:
124+
ssl_context = None
125+
89126
session = PipSession(
90-
cache=(
91-
os.path.join(options.cache_dir, "http") if options.cache_dir else None
92-
),
127+
cache=os.path.join(cache_dir, "http") if cache_dir else None,
93128
retries=retries if retries is not None else options.retries,
94129
trusted_hosts=options.trusted_hosts,
95130
index_urls=self._get_index_urls(options),
131+
ssl_context=ssl_context,
96132
)
97133

98134
# Handle custom ca-bundles from the user
@@ -142,7 +178,14 @@ def handle_pip_version_check(self, options: Values) -> None:
142178

143179
# Otherwise, check if we're using the latest version of pip available.
144180
session = self._build_session(
145-
options, retries=0, timeout=min(5, options.timeout)
181+
options,
182+
retries=0,
183+
timeout=min(5, options.timeout),
184+
# This is set to ensure the function does not fail when truststore is
185+
# specified in use-feature but cannot be loaded. This usually raises a
186+
# CommandError and shows a nice user-facing error, but this function is not
187+
# called in that try-except block.
188+
fallback_to_certifi=True,
146189
)
147190
with session:
148191
pip_self_version_check(session, options)

src/pip/_internal/index/collector.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
The main purpose of this module is to expose LinkCollector.collect_sources().
33
"""
44

5-
import cgi
65
import collections
6+
import email.message
77
import functools
88
import itertools
99
import logging
@@ -155,9 +155,11 @@ def _get_html_response(url: str, session: PipSession) -> Response:
155155
def _get_encoding_from_headers(headers: ResponseHeaders) -> Optional[str]:
156156
"""Determine if we have any encoding information in our headers."""
157157
if headers and "Content-Type" in headers:
158-
content_type, params = cgi.parse_header(headers["Content-Type"])
159-
if "charset" in params:
160-
return params["charset"]
158+
m = email.message.Message()
159+
m["content-type"] = headers["Content-Type"]
160+
charset = m.get_param("charset")
161+
if charset:
162+
return str(charset)
161163
return None
162164

163165

src/pip/_internal/network/download.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Download files with progress indicators.
22
"""
3-
import cgi
3+
import email.message
44
import logging
55
import mimetypes
66
import os
@@ -81,12 +81,13 @@ def parse_content_disposition(content_disposition: str, default_filename: str) -
8181
Parse the "filename" value from a Content-Disposition header, and
8282
return the default filename if the result is empty.
8383
"""
84-
_type, params = cgi.parse_header(content_disposition)
85-
filename = params.get("filename")
84+
m = email.message.Message()
85+
m["content-type"] = content_disposition
86+
filename = m.get_param("filename")
8687
if filename:
8788
# We need to sanitize the filename to prevent directory traversal
8889
# in case the filename contains ".." path parts.
89-
filename = sanitize_content_filename(filename)
90+
filename = sanitize_content_filename(str(filename))
9091
return filename or default_filename
9192

9293

0 commit comments

Comments
 (0)