-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8114 from uranusjr/importlib-metadata
- Loading branch information
Showing
7 changed files
with
271 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING | ||
|
||
if MYPY_CHECK_RUNNING: | ||
from typing import List, Optional | ||
|
||
from .base import BaseEnvironment | ||
|
||
|
||
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 | ||
|
||
return Environment.from_paths(paths) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
from pip._internal.utils.misc import stdlib_pkgs # TODO: Move definition here. | ||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING | ||
|
||
if MYPY_CHECK_RUNNING: | ||
from typing import Container, Iterator, List, Optional | ||
|
||
|
||
class BaseDistribution: | ||
@property | ||
def canonical_name(self): | ||
# type: () -> str | ||
raise NotImplementedError() | ||
|
||
@property | ||
def installer(self): | ||
# type: () -> str | ||
raise NotImplementedError() | ||
|
||
@property | ||
def editable(self): | ||
# type: () -> bool | ||
raise NotImplementedError() | ||
|
||
@property | ||
def local(self): | ||
# type: () -> bool | ||
raise NotImplementedError() | ||
|
||
@property | ||
def in_usersite(self): | ||
# type: () -> bool | ||
raise NotImplementedError() | ||
|
||
|
||
class BaseEnvironment: | ||
"""An environment containing distributions to introspect.""" | ||
|
||
@classmethod | ||
def default(cls): | ||
# type: () -> BaseEnvironment | ||
raise NotImplementedError() | ||
|
||
@classmethod | ||
def from_paths(cls, paths): | ||
# type: (Optional[List[str]]) -> BaseEnvironment | ||
raise NotImplementedError() | ||
|
||
def get_distribution(self, name): | ||
# type: (str) -> Optional[BaseDistribution] | ||
"""Given a requirement name, return the installed distributions.""" | ||
raise NotImplementedError() | ||
|
||
def iter_distributions(self): | ||
# type: () -> Iterator[BaseDistribution] | ||
"""Iterate through installed distributions.""" | ||
raise NotImplementedError() | ||
|
||
def iter_installed_distributions( | ||
self, | ||
local_only=True, # type: bool | ||
skip=stdlib_pkgs, # type: Container[str] | ||
include_editables=True, # type: bool | ||
editables_only=False, # type: bool | ||
user_only=False, # type: bool | ||
): | ||
# type: (...) -> Iterator[BaseDistribution] | ||
"""Return a list of installed distributions. | ||
:param local_only: If True (default), only return installations | ||
local to the current virtualenv, if in a virtualenv. | ||
:param skip: An iterable of canonicalized project names to ignore; | ||
defaults to ``stdlib_pkgs``. | ||
:param include_editables: If False, don't report editables. | ||
:param editables_only: If True, only report editables. | ||
:param user_only: If True, only report installations in the user | ||
site directory. | ||
""" | ||
it = self.iter_distributions() | ||
if local_only: | ||
it = (d for d in it if d.local) | ||
if not include_editables: | ||
it = (d for d in it if not d.editable) | ||
if editables_only: | ||
it = (d for d in it if d.editable) | ||
if user_only: | ||
it = (d for d in it if d.in_usersite) | ||
return (d for d in it if d.canonical_name not in skip) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
from pip._vendor import pkg_resources | ||
from pip._vendor.packaging.utils import canonicalize_name | ||
|
||
from pip._internal.utils import misc # TODO: Move definition here. | ||
from pip._internal.utils.packaging import get_installer | ||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING | ||
|
||
from .base import BaseDistribution, BaseEnvironment | ||
|
||
if MYPY_CHECK_RUNNING: | ||
from typing import Iterator, List, Optional | ||
|
||
|
||
class Distribution(BaseDistribution): | ||
def __init__(self, dist): | ||
# type: (pkg_resources.Distribution) -> None | ||
self._dist = dist | ||
|
||
@property | ||
def canonical_name(self): | ||
# type: () -> str | ||
return canonicalize_name(self._dist.project_name) | ||
|
||
@property | ||
def installer(self): | ||
# type: () -> str | ||
return get_installer(self._dist) | ||
|
||
@property | ||
def editable(self): | ||
# type: () -> bool | ||
return misc.dist_is_editable(self._dist) | ||
|
||
@property | ||
def local(self): | ||
# type: () -> bool | ||
return misc.dist_is_local(self._dist) | ||
|
||
@property | ||
def in_usersite(self): | ||
# type: () -> bool | ||
return misc.dist_in_usersite(self._dist) | ||
|
||
|
||
class Environment(BaseEnvironment): | ||
def __init__(self, ws): | ||
# type: (pkg_resources.WorkingSet) -> None | ||
self._ws = ws | ||
|
||
@classmethod | ||
def default(cls): | ||
# type: () -> BaseEnvironment | ||
return cls(pkg_resources.working_set) | ||
|
||
@classmethod | ||
def from_paths(cls, paths): | ||
# type: (Optional[List[str]]) -> BaseEnvironment | ||
return cls(pkg_resources.WorkingSet(paths)) | ||
|
||
def _search_distribution(self, name): | ||
# type: (str) -> Optional[BaseDistribution] | ||
"""Find a distribution matching the ``name`` in the environment. | ||
This searches from *all* distributions available in the environment, to | ||
match the behavior of ``pkg_resources.get_distribution()``. | ||
""" | ||
canonical_name = canonicalize_name(name) | ||
for dist in self.iter_distributions(): | ||
if dist.canonical_name == canonical_name: | ||
return dist | ||
return None | ||
|
||
def get_distribution(self, name): | ||
# type: (str) -> Optional[BaseDistribution] | ||
|
||
# Search the distribution by looking through the working set. | ||
dist = self._search_distribution(name) | ||
if dist: | ||
return dist | ||
|
||
# If distribution could not be found, call working_set.require to | ||
# update the working set, and try to find the distribution again. | ||
# This might happen for e.g. when you install a package twice, once | ||
# using setup.py develop and again using setup.py install. Now when | ||
# running pip uninstall twice, the package gets removed from the | ||
# working set in the first uninstall, so we have to populate the | ||
# working set again so that pip knows about it and the packages gets | ||
# picked up and is successfully uninstalled the second time too. | ||
try: | ||
# We didn't pass in any version specifiers, so this can never | ||
# raise pkg_resources.VersionConflict. | ||
self._ws.require(name) | ||
except pkg_resources.DistributionNotFound: | ||
return None | ||
return self._search_distribution(name) | ||
|
||
def iter_distributions(self): | ||
# type: () -> Iterator[BaseDistribution] | ||
for dist in self._ws: | ||
yield Distribution(dist) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.