From 11e37aa6e1b3afd6a8235327b64122b8398232a1 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Tue, 19 Jan 2021 08:13:57 +0800 Subject: [PATCH] Separate default and ad-hoc environment APIs pkg_resources performs annoying caching that needs to be worked around in some parts of pip. This makes it easier to represent the difference between environments backend by WorkingSet() and working_set. --- src/pip/_internal/metadata/__init__.py | 23 ++++++++++++++++++--- src/pip/_internal/metadata/base.py | 2 +- src/pip/_internal/metadata/pkg_resources.py | 2 +- src/pip/_internal/self_outdated_check.py | 4 ++-- src/pip/_internal/utils/misc.py | 13 ++++++++---- tests/unit/test_self_check_outdated.py | 2 +- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/pip/_internal/metadata/__init__.py b/src/pip/_internal/metadata/__init__.py index 84e91d6aebc..da2c4355dfc 100644 --- a/src/pip/_internal/metadata/__init__.py +++ b/src/pip/_internal/metadata/__init__.py @@ -6,10 +6,27 @@ from .base import BaseEnvironment -def get_environment(paths=None): +def get_default_environment(): + # type: () -> BaseEnvironment + """Get the default representation for the current environment. + + This returns an Environment instance from the chosen backend. The default + Environment instance should be built from ``sys.path`` and may use caching + to share instance state accorss calls. + """ + from .pkg_resources import Environment + + return Environment.default() + + +def get_environment(paths): # type: (Optional[List[str]]) -> BaseEnvironment + """Get a representation of the environment specified by ``paths``. + + This returns an Environment instance from the chosen backend based on the + given import paths. The backend must build a fresh instance representing + the state of installed distributions when this function is called. + """ from .pkg_resources import Environment - if paths is None: - return Environment.default() return Environment.from_paths(paths) diff --git a/src/pip/_internal/metadata/base.py b/src/pip/_internal/metadata/base.py index 0d4de00764d..955682545cc 100644 --- a/src/pip/_internal/metadata/base.py +++ b/src/pip/_internal/metadata/base.py @@ -42,7 +42,7 @@ def default(cls): @classmethod def from_paths(cls, paths): - # type: (List[str]) -> BaseEnvironment + # type: (Optional[List[str]]) -> BaseEnvironment raise NotImplementedError() def get_distribution(self, name): diff --git a/src/pip/_internal/metadata/pkg_resources.py b/src/pip/_internal/metadata/pkg_resources.py index 1e73b79f40e..d9db2955159 100644 --- a/src/pip/_internal/metadata/pkg_resources.py +++ b/src/pip/_internal/metadata/pkg_resources.py @@ -54,7 +54,7 @@ def default(cls): @classmethod def from_paths(cls, paths): - # type: (List[str]) -> BaseEnvironment + # type: (Optional[List[str]]) -> BaseEnvironment return cls(pkg_resources.WorkingSet(paths)) def _search_distribution(self, name): diff --git a/src/pip/_internal/self_outdated_check.py b/src/pip/_internal/self_outdated_check.py index 1819886591c..e8c8282cbf9 100644 --- a/src/pip/_internal/self_outdated_check.py +++ b/src/pip/_internal/self_outdated_check.py @@ -10,7 +10,7 @@ from pip._internal.index.collector import LinkCollector from pip._internal.index.package_finder import PackageFinder -from pip._internal.metadata import get_environment +from pip._internal.metadata import get_default_environment from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace from pip._internal.utils.misc import ensure_dir, get_installed_version @@ -103,7 +103,7 @@ def was_installed_by_pip(pkg): This is used not to display the upgrade message when pip is in fact installed by system package manager, such as dnf on Fedora. """ - dist = get_environment().get_distribution(pkg) + dist = get_default_environment().get_distribution(pkg) return dist is not None and "pip" == dist.installer diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index 0cee9156574..5214e9dd79b 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -405,9 +405,14 @@ def get_installed_distributions( Left for compatibility until direct pkg_resources uses are refactored out. """ - from pip._internal.metadata import get_environment + from pip._internal.metadata import get_default_environment, get_environment from pip._internal.metadata.pkg_resources import Distribution as _Dist - dists = get_environment(paths).iter_installed_distributions( + + if paths is None: + env = get_default_environment() + else: + env = get_environment(paths) + dists = env.iter_installed_distributions( local_only=local_only, skip=skip, include_editables=include_editables, @@ -426,9 +431,9 @@ def get_distribution(req_name): Left for compatibility until direct pkg_resources uses are refactored out. """ - from pip._internal.metadata import get_environment + from pip._internal.metadata import get_default_environment from pip._internal.metadata.pkg_resources import Distribution as _Dist - dist = get_environment().get_distribution(req_name) + dist = get_default_environment().get_distribution(req_name) if dist is None: return None return cast(_Dist, dist)._dist diff --git a/tests/unit/test_self_check_outdated.py b/tests/unit/test_self_check_outdated.py index 35a94a36b0f..2e8663a9c01 100644 --- a/tests/unit/test_self_check_outdated.py +++ b/tests/unit/test_self_check_outdated.py @@ -105,7 +105,7 @@ def test_pip_self_version_check(monkeypatch, stored_time, installed_ver, pretend.call_recorder(lambda *a, **kw: None)) monkeypatch.setattr(logger, 'debug', pretend.call_recorder(lambda s, exc_info=None: None)) - monkeypatch.setattr(self_outdated_check, 'get_environment', + monkeypatch.setattr(self_outdated_check, 'get_default_environment', lambda: MockEnvironment(installer)) fake_state = pretend.stub(