Skip to content

Commit

Permalink
Merge pull request #5350 from pypa/issue-5349
Browse files Browse the repository at this point in the history
Safer usages of pkg_resources
  • Loading branch information
oz123 authored Sep 14, 2022
2 parents 9572c31 + 4f49739 commit 62b57b9
Show file tree
Hide file tree
Showing 30 changed files with 123 additions and 284 deletions.
1 change: 1 addition & 0 deletions news/5349.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Modernize ``pipenv`` path patch with ``importlib.util`` to eliminate import of ``pkg_resources``
30 changes: 3 additions & 27 deletions pipenv/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import contextlib
import importlib
import importlib.util
import itertools
import json
import operator
Expand All @@ -11,12 +12,11 @@
from pathlib import Path
from sysconfig import get_paths, get_python_version, get_scheme_names

import pkg_resources

import pipenv
from pipenv.patched.pip._internal.commands.install import InstallCommand
from pipenv.patched.pip._internal.index.package_finder import PackageFinder
from pipenv.patched.pip._internal.req.req_uninstall import UninstallPathSet
from pipenv.patched.pip._vendor import pkg_resources
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
from pipenv.utils.constants import is_type_checking
from pipenv.utils.indexes import prepare_pip_source_args
Expand All @@ -41,7 +41,6 @@
from pipenv.project import Project, TPipfile, TSource

BASE_WORKING_SET = pkg_resources.WorkingSet(sys.path)
# TODO: Unittests for this class


class Environment:
Expand Down Expand Up @@ -863,7 +862,7 @@ def run_activate_this(self):
exec(code, dict(__file__=activate_this))

@contextlib.contextmanager
def activated(self, include_extras=True, extra_dists=None):
def activated(self):
"""Helper context manager to activate the environment.
This context manager will set the following variables for the duration
Expand All @@ -882,16 +881,8 @@ def activated(self, include_extras=True, extra_dists=None):
to `os.environ["PATH"]` to ensure that calls to `~Environment.run()` use the
environment's path preferentially.
"""

if not extra_dists:
extra_dists = []
original_path = sys.path
original_prefix = sys.prefix
parent_path = Path(__file__).absolute().parent
vendor_dir = parent_path.joinpath("vendor").as_posix()
patched_dir = parent_path.joinpath("patched").as_posix()
parent_path = parent_path.as_posix()
self.add_dist("pip")
prefix = self.prefix.as_posix()
with vistir.contextmanagers.temp_environ(), vistir.contextmanagers.temp_path():
os.environ["PATH"] = os.pathsep.join(
Expand All @@ -914,21 +905,6 @@ def activated(self, include_extras=True, extra_dists=None):
os.environ.pop("PYTHONHOME", None)
sys.path = self.sys_path
sys.prefix = self.sys_prefix
site.addsitedir(self.base_paths["purelib"])
pip = self.safe_import("pip") # noqa
pip_vendor = self.safe_import("pip._vendor")
pep517_dir = os.path.join(os.path.dirname(pip_vendor.__file__), "pep517")
site.addsitedir(pep517_dir)
os.environ["PYTHONPATH"] = os.pathsep.join(
[os.environ.get("PYTHONPATH", self.base_paths["PYTHONPATH"]), pep517_dir]
)
if include_extras:
site.addsitedir(parent_path)
sys.path.extend([parent_path, patched_dir, vendor_dir])
extra_dists = list(self.extra_dists) + extra_dists
for extra_dist in extra_dists:
if extra_dist not in self.get_working_set():
extra_dist.activate(self.sys_path)
try:
yield
finally:
Expand Down
19 changes: 0 additions & 19 deletions pipenv/patched/pip/_vendor/pkg_resources/LICENSE

This file was deleted.

4 changes: 2 additions & 2 deletions pipenv/patched/safety/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def check(key, db, json, full_report, bare, stdin, files, cache, ignore, output,
elif stdin:
packages = list(read_requirements(sys.stdin))
else:
import pkg_resources
import pipenv.patched.pip._vendor.pkg_resources as pkg_resources
packages = [
d for d in pkg_resources.working_set
if d.key not in {"python", "wsgiref", "argparse"}
Expand Down Expand Up @@ -150,7 +150,7 @@ def license(key, db, json, bare, cache, files, proxyprotocol, proxyhost, proxypo
if files:
packages = list(itertools.chain.from_iterable(read_requirements(f, resolve=True) for f in files))
else:
import pkg_resources
import pipenv.patched.pip._vendor.pkg_resources as pkg_resources
packages = [
d for d in pkg_resources.working_set
if d.key not in {"python", "wsgiref", "argparse"}
Expand Down
9 changes: 2 additions & 7 deletions pipenv/project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import annotations

import base64
Expand All @@ -23,6 +22,7 @@
from pipenv.environment import Environment
from pipenv.environments import Setting, is_in_virtualenv, normalize_pipfile_path
from pipenv.patched.pip._internal.commands.install import InstallCommand
from pipenv.patched.pip._vendor import pkg_resources
from pipenv.utils.constants import is_type_checking
from pipenv.utils.dependencies import (
get_canonical_names,
Expand All @@ -37,6 +37,7 @@
find_windows_executable,
get_workon_home,
is_virtual_environment,
load_path,
looks_like_dir,
safe_expandvars,
system_which,
Expand All @@ -55,8 +56,6 @@
if is_type_checking():
from typing import Dict, List, Optional, Set, Text, Tuple, Union

import pkg_resources

TSource = Dict[Text, Union[Text, bool]]
TPackageEntry = Dict[str, Union[bool, str, List[str]]]
TPackage = Dict[str, TPackageEntry]
Expand Down Expand Up @@ -243,11 +242,7 @@ def get_location_for_virtualenv(self) -> str:

@property
def working_set(self) -> pkg_resources.WorkingSet:
from pipenv.utils.shell import load_path

sys_path = load_path(self.which("python"))
import pkg_resources

return pkg_resources.WorkingSet(sys_path)

@property
Expand Down
69 changes: 9 additions & 60 deletions pipenv/resolver.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import importlib.util
import json
import logging
import os
Expand All @@ -6,65 +7,13 @@
os.environ["PIP_PYTHON_PATH"] = str(sys.executable)


def find_site_path(pkg, site_dir=None):
import pkg_resources

if site_dir is None:
site_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
working_set = pkg_resources.WorkingSet([site_dir] + sys.path[:])
for dist in working_set:
root = dist.location
base_name = dist.project_name if dist.project_name else dist.key
name = None
if "top_level.txt" in dist.metadata_listdir(""):
name = next(
iter(
[
line.strip()
for line in dist.get_metadata_lines("top_level.txt")
if line is not None
]
),
None,
)
if name is None:
name = pkg_resources.safe_name(base_name).replace("-", "_")
if not any(pkg == _ for _ in [base_name, name]):
continue
path_options = [name, f"{name}.py"]
path_options = [os.path.join(root, p) for p in path_options if p is not None]
path = next(iter(p for p in path_options if os.path.exists(p)), None)
if path is not None:
return dist, path
return None, None


def _patch_path(pipenv_site=None):
import site

pipenv_libdir = os.path.dirname(os.path.abspath(__file__))
pipenv_site_dir = os.path.dirname(pipenv_libdir)
if pipenv_site is not None:
pipenv_dist, pipenv_path = find_site_path("pipenv", site_dir=pipenv_site)
else:
pipenv_dist, pipenv_path = find_site_path("pipenv", site_dir=pipenv_site_dir)
if pipenv_dist is not None:
pipenv_dist.activate()
else:
site.addsitedir(
next(
iter(
sitedir
for sitedir in (pipenv_site, pipenv_site_dir)
if sitedir is not None
),
None,
)
)
if pipenv_path is not None:
pipenv_libdir = pipenv_path
for _dir in ("vendor", "patched", pipenv_libdir):
sys.path.insert(0, os.path.join(pipenv_libdir, _dir))
def _ensure_modules():
spec = importlib.util.spec_from_file_location(
"pipenv", location=os.path.join(os.path.dirname(__file__), "__init__.py")
)
pipenv = importlib.util.module_from_spec(spec)
sys.modules["pipenv"] = pipenv
spec.loader.exec_module(pipenv)


def get_parser():
Expand Down Expand Up @@ -838,7 +787,7 @@ def _main(
def main(argv=None):
parser = get_parser()
parsed, remaining = parser.parse_known_args(argv)
_patch_path(pipenv_site=parsed.pipenv_site)
_ensure_modules()
import warnings

from pipenv.vendor.vistir.misc import replace_with_text_stream
Expand Down
3 changes: 1 addition & 2 deletions pipenv/utils/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from pipenv.patched.pip._internal.req.req_file import parse_requirements
from pipenv.patched.pip._internal.utils.hashes import FAVORITE_HASH
from pipenv.patched.pip._internal.utils.temp_dir import global_tempdir_manager
from pipenv.patched.pip._vendor import pkg_resources
from pipenv.project import Project
from pipenv.vendor import click
from pipenv.vendor.requirementslib import Requirement
Expand Down Expand Up @@ -1147,8 +1148,6 @@ def resolve_deps(

@lru_cache()
def get_pipenv_sitedir() -> Optional[str]:
import pkg_resources

site_dir = next(
iter(d for d in pkg_resources.working_set if d.key.lower() == "pipenv"), None
)
Expand Down
2 changes: 1 addition & 1 deletion pipenv/vendor/cerberus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from __future__ import absolute_import

from pkg_resources import get_distribution, DistributionNotFound
from pipenv.patched.pip._vendor.pkg_resources import get_distribution, DistributionNotFound

from pipenv.vendor.cerberus.validator import DocumentError, Validator
from pipenv.vendor.cerberus.schema import rules_set_registry, schema_registry, SchemaError
Expand Down
2 changes: 1 addition & 1 deletion pipenv/vendor/cerberus/tests/test_assorted.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

from decimal import Decimal
from pkg_resources import Distribution, DistributionNotFound
from pipenv.patched.pip._vendor.pkg_resources import Distribution, DistributionNotFound

from pytest import mark

Expand Down
2 changes: 1 addition & 1 deletion pipenv/vendor/requirementslib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .models.pipfile import Pipfile
from .models.requirements import Requirement

__version__ = "2.0.1"
__version__ = "2.0.2"


logger = logging.getLogger(__name__)
Expand Down
3 changes: 0 additions & 3 deletions pipenv/vendor/requirementslib/environment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# -*- coding=utf-8 -*-
from __future__ import absolute_import, print_function

import os

from pipenv.vendor.platformdirs import user_cache_dir
Expand Down
4 changes: 1 addition & 3 deletions pipenv/vendor/requirementslib/models/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import hashlib
import json
import os
import pathlib
import sys

import pipenv.vendor.vistir as vistir
from pipenv.patched.pip._vendor.packaging.requirements import Requirement
from pipenv.patched.pip._internal.utils.hashes import FAVORITE_HASH
from pipenv.patched.pip._internal.vcs.versioncontrol import VcsSupport
from pipenv.patched.pip._vendor.cachecontrol.cache import DictCache
from pipenv.patched.pip._vendor.packaging.requirements import Requirement
from pipenv.vendor.platformdirs import user_cache_dir

from .utils import as_tuple, get_pinned_version, key_from_req, lookup_table
Expand Down
27 changes: 6 additions & 21 deletions pipenv/vendor/requirementslib/models/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
# -*- coding=utf-8 -*-

import atexit
import contextlib
import copy
import functools
import os
from contextlib import ExitStack
from json import JSONDecodeError

import pipenv.vendor.attr as attr
import pipenv.patched.pip._vendor.requests as requests
from pipenv.patched.pip._vendor.packaging.markers import Marker
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
from pipenv.patched.pip._vendor.packaging.version import parse
from pipenv.patched.pip._internal.cache import WheelCache
from pipenv.patched.pip._internal.models.format_control import FormatControl
from pipenv.patched.pip._internal.operations.build.build_tracker import get_build_tracker
from pipenv.patched.pip._internal.req.constructors import install_req_from_line
from pipenv.patched.pip._internal.req.req_install import InstallRequirement
from pipenv.patched.pip._internal.req.req_set import RequirementSet
from pipenv.patched.pip._internal.utils.temp_dir import TempDirectory, global_tempdir_manager
from pipenv.patched.pip._vendor.packaging.markers import Marker
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
from pipenv.patched.pip._vendor.packaging.version import parse
from pipenv.vendor.vistir.compat import fs_str
from pipenv.vendor.vistir.contextmanagers import temp_environ
from pipenv.vendor.vistir.path import create_tracked_tempdir
Expand All @@ -35,7 +32,6 @@
from .setup_info import SetupInfo
from .utils import (
clean_requires_python,
fix_requires_python_marker,
format_requirement,
full_groupby,
is_pinned_requirement,
Expand All @@ -46,23 +42,12 @@
)

if MYPY_RUNNING:
from typing import (
Any,
Dict,
Generator,
List,
Optional,
Set,
Text,
Tuple,
TypeVar,
Union,
)
from typing import Any, Dict, List, Optional, Set, Text, TypeVar, Union

from pipenv.patched.pip._vendor.packaging.requirements import Requirement as PackagingRequirement
from pipenv.patched.pip._internal.commands.base_command import Command
from pipenv.patched.pip._internal.commands import Command
from pipenv.patched.pip._internal.index.package_finder import PackageFinder
from pipenv.patched.pip._internal.models.candidate import InstallationCandidate
from pipenv.patched.pip._vendor.packaging.requirements import Requirement as PackagingRequirement

TRequirement = TypeVar("TRequirement")
RequirementType = TypeVar(
Expand Down
3 changes: 0 additions & 3 deletions pipenv/vendor/requirementslib/models/lockfile.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function

import copy
import itertools
import os
Expand Down
Loading

0 comments on commit 62b57b9

Please sign in to comment.