comparison planemo/lib/python3.7/site-packages/pip/_internal/models/link.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 import posixpath
2 import re
3
4 from pip._vendor.six.moves.urllib import parse as urllib_parse
5
6 from pip._internal.utils.misc import (
7 WHEEL_EXTENSION, path_to_url, redact_password_from_url,
8 split_auth_from_netloc, splitext,
9 )
10 from pip._internal.utils.models import KeyBasedCompareMixin
11 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
12
13 if MYPY_CHECK_RUNNING:
14 from typing import Optional, Text, Tuple, Union
15 from pip._internal.index import HTMLPage
16 from pip._internal.utils.hashes import Hashes
17
18
19 class Link(KeyBasedCompareMixin):
20 """Represents a parsed link from a Package Index's simple URL
21 """
22
23 def __init__(
24 self,
25 url, # type: str
26 comes_from=None, # type: Optional[Union[str, HTMLPage]]
27 requires_python=None, # type: Optional[str]
28 yanked_reason=None, # type: Optional[Text]
29 ):
30 # type: (...) -> None
31 """
32 :param url: url of the resource pointed to (href of the link)
33 :param comes_from: instance of HTMLPage where the link was found,
34 or string.
35 :param requires_python: String containing the `Requires-Python`
36 metadata field, specified in PEP 345. This may be specified by
37 a data-requires-python attribute in the HTML link tag, as
38 described in PEP 503.
39 :param yanked_reason: the reason the file has been yanked, if the
40 file has been yanked, or None if the file hasn't been yanked.
41 This is the value of the "data-yanked" attribute, if present, in
42 a simple repository HTML link. If the file has been yanked but
43 no reason was provided, this should be the empty string. See
44 PEP 592 for more information and the specification.
45 """
46
47 # url can be a UNC windows share
48 if url.startswith('\\\\'):
49 url = path_to_url(url)
50
51 self._parsed_url = urllib_parse.urlsplit(url)
52 # Store the url as a private attribute to prevent accidentally
53 # trying to set a new value.
54 self._url = url
55
56 self.comes_from = comes_from
57 self.requires_python = requires_python if requires_python else None
58 self.yanked_reason = yanked_reason
59
60 super(Link, self).__init__(key=url, defining_class=Link)
61
62 def __str__(self):
63 if self.requires_python:
64 rp = ' (requires-python:%s)' % self.requires_python
65 else:
66 rp = ''
67 if self.comes_from:
68 return '%s (from %s)%s' % (redact_password_from_url(self._url),
69 self.comes_from, rp)
70 else:
71 return redact_password_from_url(str(self._url))
72
73 def __repr__(self):
74 return '<Link %s>' % self
75
76 @property
77 def url(self):
78 # type: () -> str
79 return self._url
80
81 @property
82 def filename(self):
83 # type: () -> str
84 path = self.path.rstrip('/')
85 name = posixpath.basename(path)
86 if not name:
87 # Make sure we don't leak auth information if the netloc
88 # includes a username and password.
89 netloc, user_pass = split_auth_from_netloc(self.netloc)
90 return netloc
91
92 name = urllib_parse.unquote(name)
93 assert name, ('URL %r produced no filename' % self._url)
94 return name
95
96 @property
97 def scheme(self):
98 # type: () -> str
99 return self._parsed_url.scheme
100
101 @property
102 def netloc(self):
103 # type: () -> str
104 """
105 This can contain auth information.
106 """
107 return self._parsed_url.netloc
108
109 @property
110 def path(self):
111 # type: () -> str
112 return urllib_parse.unquote(self._parsed_url.path)
113
114 def splitext(self):
115 # type: () -> Tuple[str, str]
116 return splitext(posixpath.basename(self.path.rstrip('/')))
117
118 @property
119 def ext(self):
120 # type: () -> str
121 return self.splitext()[1]
122
123 @property
124 def url_without_fragment(self):
125 # type: () -> str
126 scheme, netloc, path, query, fragment = self._parsed_url
127 return urllib_parse.urlunsplit((scheme, netloc, path, query, None))
128
129 _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)')
130
131 @property
132 def egg_fragment(self):
133 # type: () -> Optional[str]
134 match = self._egg_fragment_re.search(self._url)
135 if not match:
136 return None
137 return match.group(1)
138
139 _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)')
140
141 @property
142 def subdirectory_fragment(self):
143 # type: () -> Optional[str]
144 match = self._subdirectory_fragment_re.search(self._url)
145 if not match:
146 return None
147 return match.group(1)
148
149 _hash_re = re.compile(
150 r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)'
151 )
152
153 @property
154 def hash(self):
155 # type: () -> Optional[str]
156 match = self._hash_re.search(self._url)
157 if match:
158 return match.group(2)
159 return None
160
161 @property
162 def hash_name(self):
163 # type: () -> Optional[str]
164 match = self._hash_re.search(self._url)
165 if match:
166 return match.group(1)
167 return None
168
169 @property
170 def show_url(self):
171 # type: () -> Optional[str]
172 return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0])
173
174 @property
175 def is_wheel(self):
176 # type: () -> bool
177 return self.ext == WHEEL_EXTENSION
178
179 @property
180 def is_artifact(self):
181 # type: () -> bool
182 """
183 Determines if this points to an actual artifact (e.g. a tarball) or if
184 it points to an "abstract" thing like a path or a VCS location.
185 """
186 from pip._internal.vcs import vcs
187
188 if self.scheme in vcs.all_schemes:
189 return False
190
191 return True
192
193 @property
194 def is_yanked(self):
195 # type: () -> bool
196 return self.yanked_reason is not None
197
198 @property
199 def has_hash(self):
200 return self.hash_name is not None
201
202 def is_hash_allowed(self, hashes):
203 # type: (Optional[Hashes]) -> bool
204 """
205 Return True if the link has a hash and it is allowed.
206 """
207 if hashes is None or not self.has_hash:
208 return False
209 # Assert non-None so mypy knows self.hash_name and self.hash are str.
210 assert self.hash_name is not None
211 assert self.hash is not None
212
213 return hashes.is_hash_allowed(self.hash_name, hex_digest=self.hash)