comparison lib/python3.8/site-packages/pip/_internal/models/link.py @ 0:9e54283cc701 draft

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