comparison planemo/lib/python3.7/site-packages/pip/_internal/operations/freeze.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 from __future__ import absolute_import
2
3 import collections
4 import logging
5 import os
6 import re
7
8 from pip._vendor import six
9 from pip._vendor.packaging.utils import canonicalize_name
10 from pip._vendor.pkg_resources import RequirementParseError
11
12 from pip._internal.exceptions import BadCommand, InstallationError
13 from pip._internal.req.constructors import (
14 install_req_from_editable, install_req_from_line,
15 )
16 from pip._internal.req.req_file import COMMENT_RE
17 from pip._internal.utils.misc import (
18 dist_is_editable, get_installed_distributions,
19 )
20 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
21
22 if MYPY_CHECK_RUNNING:
23 from typing import (
24 Iterator, Optional, List, Container, Set, Dict, Tuple, Iterable, Union
25 )
26 from pip._internal.cache import WheelCache
27 from pip._vendor.pkg_resources import (
28 Distribution, Requirement
29 )
30
31 RequirementInfo = Tuple[Optional[Union[str, Requirement]], bool, List[str]]
32
33
34 logger = logging.getLogger(__name__)
35
36
37 def freeze(
38 requirement=None, # type: Optional[List[str]]
39 find_links=None, # type: Optional[List[str]]
40 local_only=None, # type: Optional[bool]
41 user_only=None, # type: Optional[bool]
42 paths=None, # type: Optional[List[str]]
43 skip_regex=None, # type: Optional[str]
44 isolated=False, # type: bool
45 wheel_cache=None, # type: Optional[WheelCache]
46 exclude_editable=False, # type: bool
47 skip=() # type: Container[str]
48 ):
49 # type: (...) -> Iterator[str]
50 find_links = find_links or []
51 skip_match = None
52
53 if skip_regex:
54 skip_match = re.compile(skip_regex).search
55
56 for link in find_links:
57 yield '-f %s' % link
58 installations = {} # type: Dict[str, FrozenRequirement]
59 for dist in get_installed_distributions(local_only=local_only,
60 skip=(),
61 user_only=user_only,
62 paths=paths):
63 try:
64 req = FrozenRequirement.from_dist(dist)
65 except RequirementParseError as exc:
66 # We include dist rather than dist.project_name because the
67 # dist string includes more information, like the version and
68 # location. We also include the exception message to aid
69 # troubleshooting.
70 logger.warning(
71 'Could not generate requirement for distribution %r: %s',
72 dist, exc
73 )
74 continue
75 if exclude_editable and req.editable:
76 continue
77 installations[req.name] = req
78
79 if requirement:
80 # the options that don't get turned into an InstallRequirement
81 # should only be emitted once, even if the same option is in multiple
82 # requirements files, so we need to keep track of what has been emitted
83 # so that we don't emit it again if it's seen again
84 emitted_options = set() # type: Set[str]
85 # keep track of which files a requirement is in so that we can
86 # give an accurate warning if a requirement appears multiple times.
87 req_files = collections.defaultdict(list) # type: Dict[str, List[str]]
88 for req_file_path in requirement:
89 with open(req_file_path) as req_file:
90 for line in req_file:
91 if (not line.strip() or
92 line.strip().startswith('#') or
93 (skip_match and skip_match(line)) or
94 line.startswith((
95 '-r', '--requirement',
96 '-Z', '--always-unzip',
97 '-f', '--find-links',
98 '-i', '--index-url',
99 '--pre',
100 '--trusted-host',
101 '--process-dependency-links',
102 '--extra-index-url'))):
103 line = line.rstrip()
104 if line not in emitted_options:
105 emitted_options.add(line)
106 yield line
107 continue
108
109 if line.startswith('-e') or line.startswith('--editable'):
110 if line.startswith('-e'):
111 line = line[2:].strip()
112 else:
113 line = line[len('--editable'):].strip().lstrip('=')
114 line_req = install_req_from_editable(
115 line,
116 isolated=isolated,
117 wheel_cache=wheel_cache,
118 )
119 else:
120 line_req = install_req_from_line(
121 COMMENT_RE.sub('', line).strip(),
122 isolated=isolated,
123 wheel_cache=wheel_cache,
124 )
125
126 if not line_req.name:
127 logger.info(
128 "Skipping line in requirement file [%s] because "
129 "it's not clear what it would install: %s",
130 req_file_path, line.strip(),
131 )
132 logger.info(
133 " (add #egg=PackageName to the URL to avoid"
134 " this warning)"
135 )
136 elif line_req.name not in installations:
137 # either it's not installed, or it is installed
138 # but has been processed already
139 if not req_files[line_req.name]:
140 logger.warning(
141 "Requirement file [%s] contains %s, but "
142 "package %r is not installed",
143 req_file_path,
144 COMMENT_RE.sub('', line).strip(), line_req.name
145 )
146 else:
147 req_files[line_req.name].append(req_file_path)
148 else:
149 yield str(installations[line_req.name]).rstrip()
150 del installations[line_req.name]
151 req_files[line_req.name].append(req_file_path)
152
153 # Warn about requirements that were included multiple times (in a
154 # single requirements file or in different requirements files).
155 for name, files in six.iteritems(req_files):
156 if len(files) > 1:
157 logger.warning("Requirement %s included multiple times [%s]",
158 name, ', '.join(sorted(set(files))))
159
160 yield(
161 '## The following requirements were added by '
162 'pip freeze:'
163 )
164 for installation in sorted(
165 installations.values(), key=lambda x: x.name.lower()):
166 if canonicalize_name(installation.name) not in skip:
167 yield str(installation).rstrip()
168
169
170 def get_requirement_info(dist):
171 # type: (Distribution) -> RequirementInfo
172 """
173 Compute and return values (req, editable, comments) for use in
174 FrozenRequirement.from_dist().
175 """
176 if not dist_is_editable(dist):
177 return (None, False, [])
178
179 location = os.path.normcase(os.path.abspath(dist.location))
180
181 from pip._internal.vcs import vcs, RemoteNotFoundError
182 vcs_backend = vcs.get_backend_for_dir(location)
183
184 if vcs_backend is None:
185 req = dist.as_requirement()
186 logger.debug(
187 'No VCS found for editable requirement "%s" in: %r', req,
188 location,
189 )
190 comments = [
191 '# Editable install with no version control ({})'.format(req)
192 ]
193 return (location, True, comments)
194
195 try:
196 req = vcs_backend.get_src_requirement(location, dist.project_name)
197 except RemoteNotFoundError:
198 req = dist.as_requirement()
199 comments = [
200 '# Editable {} install with no remote ({})'.format(
201 type(vcs_backend).__name__, req,
202 )
203 ]
204 return (location, True, comments)
205
206 except BadCommand:
207 logger.warning(
208 'cannot determine version of editable source in %s '
209 '(%s command not found in path)',
210 location,
211 vcs_backend.name,
212 )
213 return (None, True, [])
214
215 except InstallationError as exc:
216 logger.warning(
217 "Error when trying to get requirement for VCS system %s, "
218 "falling back to uneditable format", exc
219 )
220 else:
221 if req is not None:
222 return (req, True, [])
223
224 logger.warning(
225 'Could not determine repository location of %s', location
226 )
227 comments = ['## !! Could not determine repository location']
228
229 return (None, False, comments)
230
231
232 class FrozenRequirement(object):
233 def __init__(self, name, req, editable, comments=()):
234 # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None
235 self.name = name
236 self.req = req
237 self.editable = editable
238 self.comments = comments
239
240 @classmethod
241 def from_dist(cls, dist):
242 # type: (Distribution) -> FrozenRequirement
243 req, editable, comments = get_requirement_info(dist)
244 if req is None:
245 req = dist.as_requirement()
246
247 return cls(dist.project_name, req, editable, comments=comments)
248
249 def __str__(self):
250 req = self.req
251 if self.editable:
252 req = '-e %s' % req
253 return '\n'.join(list(self.comments) + [str(req)]) + '\n'