1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import logging
import re
from typing import Container, Iterator, List, Optional
from pip._vendor.packaging.version import _BaseVersion
from pip._internal.utils.misc import stdlib_pkgs # TODO: Move definition here.
logger = logging.getLogger(__name__)
class BaseDistribution:
@property
def location(self):
# type: () -> Optional[str]
"""Where the distribution is loaded from.
A string value is not necessarily a filesystem path, since distributions
can be loaded from other sources, e.g. arbitrary zip archives. ``None``
means the distribution is created in-memory.
"""
raise NotImplementedError()
@property
def metadata_version(self):
# type: () -> Optional[str]
"""Value of "Metadata-Version:" in the distribution, if available."""
raise NotImplementedError()
@property
def canonical_name(self):
# type: () -> str
raise NotImplementedError()
@property
def version(self):
# type: () -> _BaseVersion
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.
This function should be implemented by subclass, but never called
directly. Use the public ``iter_distribution()`` instead, which
implements additional logic to make sure the distributions are valid.
"""
raise NotImplementedError()
def iter_distributions(self):
# type: () -> Iterator[BaseDistribution]
"""Iterate through installed distributions."""
for dist in self._iter_distributions():
# Make sure the distribution actually comes from a valid Python
# packaging distribution. Pip's AdjacentTempDirectory leaves folders
# e.g. ``~atplotlib.dist-info`` if cleanup was interrupted. The
# valid project name pattern is taken from PEP 508.
project_name_valid = re.match(
r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$",
dist.canonical_name,
flags=re.IGNORECASE,
)
if not project_name_valid:
logger.warning(
"Ignoring invalid distribution %s (%s)",
dist.canonical_name,
dist.location,
)
continue
yield dist
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)
|