Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pip 23.0 #5586

Merged
merged 6 commits into from
Feb 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pipenv/patched/patched.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pip==22.3.1
pip==23.0
safety==2.3.2
2 changes: 1 addition & 1 deletion pipenv/patched/pip/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List, Optional

__version__ = "22.3.1"
__version__ = "23.0"


def main(args: Optional[List[str]] = None) -> int:
Expand Down
15 changes: 8 additions & 7 deletions pipenv/patched/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import sys
import textwrap
from collections import OrderedDict
from sysconfig import get_paths
from types import TracebackType
from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type

Expand All @@ -18,7 +17,12 @@

from pipenv.patched.pip import __file__ as pip_location
from pipenv.patched.pip._internal.cli.spinners import open_spinner
from pipenv.patched.pip._internal.locations import get_platlib, get_prefixed_libs, get_purelib
from pipenv.patched.pip._internal.locations import (
get_isolated_environment_bin_path,
get_isolated_environment_lib_paths,
get_platlib,
get_purelib,
)
from pipenv.patched.pip._internal.metadata import get_default_environment, get_environment
from pipenv.patched.pip._internal.utils.subprocess import call_subprocess
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
Expand All @@ -33,11 +37,8 @@ class _Prefix:
def __init__(self, path: str) -> None:
self.path = path
self.setup = False
self.bin_dir = get_paths(
"nt" if os.name == "nt" else "posix_prefix",
vars={"base": path, "platbase": path},
)["scripts"]
self.lib_dirs = get_prefixed_libs(path)
self.bin_dir = get_isolated_environment_bin_path(path)
self.lib_dirs = get_isolated_environment_lib_paths(path)


def get_runnable_pip() -> str:
Expand Down
8 changes: 3 additions & 5 deletions pipenv/patched/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,11 +825,9 @@ def _handle_config_settings(
dest="install_options",
action="append",
metavar="options",
help="Extra arguments to be supplied to the setup.py install "
'command (use like --install-option="--install-scripts=/usr/local/'
'bin"). Use multiple --install-option options to pass multiple '
"options to setup.py install. If you are using an option with a "
"directory path, be sure to use absolute path.",
help="This option is deprecated. Using this option with location-changing "
"options may cause unexpected behavior. "
"Use pip-level options like --user, --prefix, --root, and --target.",
)

build_options: Callable[..., Option] = partial(
Expand Down
2 changes: 1 addition & 1 deletion pipenv/patched/pip/_internal/commands/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def create_vendor_txt_map() -> Dict[str, str]:

def get_module_from_module_name(module_name: str) -> ModuleType:
# Module name can be uppercase in vendor.txt for some reason...
module_name = module_name.lower()
module_name = module_name.lower().replace("-", "_")
# PATCH: setuptools is actually only pkg_resources.
if module_name == "setuptools":
module_name = "pkg_resources"
Expand Down
7 changes: 1 addition & 6 deletions pipenv/patched/pip/_internal/commands/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,14 @@ def add_options(self) -> None:
self.parser.insert_option_group(0, self.cmd_opts)

def run(self, options: Values, args: List[str]) -> int:
logger.warning(
"pip inspect is currently an experimental command. "
"The output format may change in a future release without prior warning."
)

cmdoptions.check_list_path_option(options)
dists = get_environment(options.path).iter_installed_distributions(
local_only=options.local,
user_only=options.user,
skip=set(stdlib_pkgs),
)
output = {
"version": "0",
"version": "1",
"pip_version": __version__,
"installed": [self._dist_to_dict(dist) for dist in dists],
"environment": default_environment(),
Expand Down
21 changes: 15 additions & 6 deletions pipenv/patched/pip/_internal/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from pipenv.patched.pip._internal.utils.filesystem import test_writable_dir
from pipenv.patched.pip._internal.utils.logging import getLogger
from pipenv.patched.pip._internal.utils.misc import (
check_externally_managed,
ensure_dir,
get_pip_version,
protect_pip_from_modification_on_windows,
Expand Down Expand Up @@ -284,6 +285,20 @@ def run(self, options: Values, args: List[str]) -> int:
if options.use_user_site and options.target_dir is not None:
raise CommandError("Can not combine '--user' and '--target'")

# Check whether the environment we're installing into is externally
# managed, as specified in PEP 668. Specifying --root, --target, or
# --prefix disables the check, since there's no reliable way to locate
# the EXTERNALLY-MANAGED file for those cases. An exception is also
# made specifically for "--dry-run --report" for convenience.
installing_into_current_environment = (
not (options.dry_run and options.json_report_file)
and options.root_path is None
and options.target_dir is None
and options.prefix_path is None
)
if installing_into_current_environment:
check_externally_managed()

upgrade_strategy = "to-satisfy-only"
if options.upgrade:
upgrade_strategy = options.upgrade_strategy
Expand Down Expand Up @@ -402,12 +417,6 @@ def run(self, options: Values, args: List[str]) -> int:
)

if options.json_report_file:
logger.warning(
"--report is currently an experimental option. "
"The output format may change in a future release "
"without prior warning."
)

report = InstallationReport(requirement_set.requirements_to_install)
if options.json_report_file == "-":
print_json(data=report.to_dict())
Expand Down
6 changes: 6 additions & 0 deletions pipenv/patched/pip/_internal/commands/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class _PackageInfo(NamedTuple):
name: str
version: str
location: str
editable_project_location: Optional[str]
requires: List[str]
required_by: List[str]
installer: str
Expand Down Expand Up @@ -120,6 +121,7 @@ def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]:
name=dist.raw_name,
version=str(dist.version),
location=dist.location or "",
editable_project_location=dist.editable_project_location,
requires=requires,
required_by=required_by,
installer=dist.installer,
Expand Down Expand Up @@ -158,6 +160,10 @@ def print_results(
write_output("Author-email: %s", dist.author_email)
write_output("License: %s", dist.license)
write_output("Location: %s", dist.location)
if dist.editable_project_location is not None:
write_output(
"Editable project location: %s", dist.editable_project_location
)
write_output("Requires: %s", ", ".join(dist.requires))
write_output("Required-by: %s", ", ".join(dist.required_by))

Expand Down
7 changes: 6 additions & 1 deletion pipenv/patched/pip/_internal/commands/uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
install_req_from_line,
install_req_from_parsed_requirement,
)
from pipenv.patched.pip._internal.utils.misc import protect_pip_from_modification_on_windows
from pipenv.patched.pip._internal.utils.misc import (
check_externally_managed,
protect_pip_from_modification_on_windows,
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -90,6 +93,8 @@ def run(self, options: Values, args: List[str]) -> int:
f'"pip help {self.name}")'
)

check_externally_managed()

protect_pip_from_modification_on_windows(
modifying_pip="pip" in reqs_to_uninstall
)
Expand Down
87 changes: 86 additions & 1 deletion pipenv/patched/pip/_internal/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
"""

import configparser
import contextlib
import locale
import logging
import pathlib
import re
import sys
from itertools import chain, groupby, repeat
from typing import TYPE_CHECKING, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union

from pipenv.patched.pip._vendor.requests.models import Request, Response
from pipenv.patched.pip._vendor.rich.console import Console, ConsoleOptions, RenderResult
Expand All @@ -22,6 +27,8 @@
from pipenv.patched.pip._internal.metadata import BaseDistribution
from pipenv.patched.pip._internal.req.req_install import InstallRequirement

logger = logging.getLogger(__name__)


#
# Scaffolding
Expand Down Expand Up @@ -658,3 +665,81 @@ def __str__(self) -> str:
assert self.error is not None
message_part = f".\n{self.error}\n"
return f"Configuration file {self.reason}{message_part}"


_DEFAULT_EXTERNALLY_MANAGED_ERROR = f"""\
The Python environment under {sys.prefix} is managed externally, and may not be
manipulated by the user. Please use specific tooling from the distributor of
the Python installation to interact with this environment instead.
"""


class ExternallyManagedEnvironment(DiagnosticPipError):
"""The current environment is externally managed.

This is raised when the current environment is externally managed, as
defined by `PEP 668`_. The ``EXTERNALLY-MANAGED`` configuration is checked
and displayed when the error is bubbled up to the user.

:param error: The error message read from ``EXTERNALLY-MANAGED``.
"""

reference = "externally-managed-environment"

def __init__(self, error: Optional[str]) -> None:
if error is None:
context = Text(_DEFAULT_EXTERNALLY_MANAGED_ERROR)
else:
context = Text(error)
super().__init__(
message="This environment is externally managed",
context=context,
note_stmt=(
"If you believe this is a mistake, please contact your "
"Python installation or OS distribution provider."
),
hint_stmt=Text("See PEP 668 for the detailed specification."),
)

@staticmethod
def _iter_externally_managed_error_keys() -> Iterator[str]:
# LC_MESSAGES is in POSIX, but not the C standard. The most common
# platform that does not implement this category is Windows, where
# using other categories for console message localization is equally
# unreliable, so we fall back to the locale-less vendor message. This
# can always be re-evaluated when a vendor proposes a new alternative.
try:
category = locale.LC_MESSAGES
except AttributeError:
lang: Optional[str] = None
else:
lang, _ = locale.getlocale(category)
if lang is not None:
yield f"Error-{lang}"
for sep in ("-", "_"):
before, found, _ = lang.partition(sep)
if not found:
continue
yield f"Error-{before}"
yield "Error"

@classmethod
def from_config(
cls,
config: Union[pathlib.Path, str],
) -> "ExternallyManagedEnvironment":
parser = configparser.ConfigParser(interpolation=None)
try:
parser.read(config, encoding="utf-8")
section = parser["externally-managed"]
for key in cls._iter_externally_managed_error_keys():
with contextlib.suppress(KeyError):
return cls(section[key])
except KeyError:
pass
except (OSError, UnicodeDecodeError, configparser.ParsingError):
from pipenv.patched.pip._internal.utils._log import VERBOSE

exc_info = logger.isEnabledFor(VERBOSE)
logger.warning("Failed to read %s", config, exc_info=exc_info)
return cls(None)
2 changes: 1 addition & 1 deletion pipenv/patched/pip/_internal/index/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ def _get_index_content(link: Link, *, session: PipSession) -> Optional["IndexCon
if not url.endswith("/"):
url += "/"
# TODO: In the future, it would be nice if pip supported PEP 691
# style respones in the file:// URLs, however there's no
# style responses in the file:// URLs, however there's no
# standard file extension for application/vnd.pypi.simple.v1+json
# so we'll need to come up with something on our own.
url = urllib.parse.urljoin(url, "index.html")
Expand Down
Loading