Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_internal/utils/compat.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 """Stuff that differs in different Python versions and platform | |
2 distributions.""" | |
3 | |
4 # The following comment should be removed at some point in the future. | |
5 # mypy: disallow-untyped-defs=False | |
6 | |
7 from __future__ import absolute_import, division | |
8 | |
9 import codecs | |
10 import locale | |
11 import logging | |
12 import os | |
13 import shutil | |
14 import sys | |
15 | |
16 from pip._vendor.six import PY2, text_type | |
17 | |
18 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
19 | |
20 if MYPY_CHECK_RUNNING: | |
21 from typing import Optional, Text, Tuple, Union | |
22 | |
23 try: | |
24 import ipaddress | |
25 except ImportError: | |
26 try: | |
27 from pip._vendor import ipaddress # type: ignore | |
28 except ImportError: | |
29 import ipaddr as ipaddress # type: ignore | |
30 ipaddress.ip_address = ipaddress.IPAddress # type: ignore | |
31 ipaddress.ip_network = ipaddress.IPNetwork # type: ignore | |
32 | |
33 | |
34 __all__ = [ | |
35 "ipaddress", "uses_pycache", "console_to_str", | |
36 "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", | |
37 ] | |
38 | |
39 | |
40 logger = logging.getLogger(__name__) | |
41 | |
42 if PY2: | |
43 import imp | |
44 | |
45 try: | |
46 cache_from_source = imp.cache_from_source # type: ignore | |
47 except AttributeError: | |
48 # does not use __pycache__ | |
49 cache_from_source = None | |
50 | |
51 uses_pycache = cache_from_source is not None | |
52 else: | |
53 uses_pycache = True | |
54 from importlib.util import cache_from_source | |
55 | |
56 | |
57 if PY2: | |
58 # In Python 2.7, backslashreplace exists | |
59 # but does not support use for decoding. | |
60 # We implement our own replace handler for this | |
61 # situation, so that we can consistently use | |
62 # backslash replacement for all versions. | |
63 def backslashreplace_decode_fn(err): | |
64 raw_bytes = (err.object[i] for i in range(err.start, err.end)) | |
65 # Python 2 gave us characters - convert to numeric bytes | |
66 raw_bytes = (ord(b) for b in raw_bytes) | |
67 return u"".join(u"\\x%x" % c for c in raw_bytes), err.end | |
68 codecs.register_error( | |
69 "backslashreplace_decode", | |
70 backslashreplace_decode_fn, | |
71 ) | |
72 backslashreplace_decode = "backslashreplace_decode" | |
73 else: | |
74 backslashreplace_decode = "backslashreplace" | |
75 | |
76 | |
77 def has_tls(): | |
78 # type: () -> bool | |
79 try: | |
80 import _ssl # noqa: F401 # ignore unused | |
81 return True | |
82 except ImportError: | |
83 pass | |
84 | |
85 from pip._vendor.urllib3.util import IS_PYOPENSSL | |
86 return IS_PYOPENSSL | |
87 | |
88 | |
89 def str_to_display(data, desc=None): | |
90 # type: (Union[bytes, Text], Optional[str]) -> Text | |
91 """ | |
92 For display or logging purposes, convert a bytes object (or text) to | |
93 text (e.g. unicode in Python 2) safe for output. | |
94 | |
95 :param desc: An optional phrase describing the input data, for use in | |
96 the log message if a warning is logged. Defaults to "Bytes object". | |
97 | |
98 This function should never error out and so can take a best effort | |
99 approach. It is okay to be lossy if needed since the return value is | |
100 just for display. | |
101 | |
102 We assume the data is in the locale preferred encoding. If it won't | |
103 decode properly, we warn the user but decode as best we can. | |
104 | |
105 We also ensure that the output can be safely written to standard output | |
106 without encoding errors. | |
107 """ | |
108 if isinstance(data, text_type): | |
109 return data | |
110 | |
111 # Otherwise, data is a bytes object (str in Python 2). | |
112 # First, get the encoding we assume. This is the preferred | |
113 # encoding for the locale, unless that is not found, or | |
114 # it is ASCII, in which case assume UTF-8 | |
115 encoding = locale.getpreferredencoding() | |
116 if (not encoding) or codecs.lookup(encoding).name == "ascii": | |
117 encoding = "utf-8" | |
118 | |
119 # Now try to decode the data - if we fail, warn the user and | |
120 # decode with replacement. | |
121 try: | |
122 decoded_data = data.decode(encoding) | |
123 except UnicodeDecodeError: | |
124 if desc is None: | |
125 desc = 'Bytes object' | |
126 msg_format = '{} does not appear to be encoded as %s'.format(desc) | |
127 logger.warning(msg_format, encoding) | |
128 decoded_data = data.decode(encoding, errors=backslashreplace_decode) | |
129 | |
130 # Make sure we can print the output, by encoding it to the output | |
131 # encoding with replacement of unencodable characters, and then | |
132 # decoding again. | |
133 # We use stderr's encoding because it's less likely to be | |
134 # redirected and if we don't find an encoding we skip this | |
135 # step (on the assumption that output is wrapped by something | |
136 # that won't fail). | |
137 # The double getattr is to deal with the possibility that we're | |
138 # being called in a situation where sys.__stderr__ doesn't exist, | |
139 # or doesn't have an encoding attribute. Neither of these cases | |
140 # should occur in normal pip use, but there's no harm in checking | |
141 # in case people use pip in (unsupported) unusual situations. | |
142 output_encoding = getattr(getattr(sys, "__stderr__", None), | |
143 "encoding", None) | |
144 | |
145 if output_encoding: | |
146 output_encoded = decoded_data.encode( | |
147 output_encoding, | |
148 errors="backslashreplace" | |
149 ) | |
150 decoded_data = output_encoded.decode(output_encoding) | |
151 | |
152 return decoded_data | |
153 | |
154 | |
155 def console_to_str(data): | |
156 # type: (bytes) -> Text | |
157 """Return a string, safe for output, of subprocess output. | |
158 """ | |
159 return str_to_display(data, desc='Subprocess output') | |
160 | |
161 | |
162 def get_path_uid(path): | |
163 # type: (str) -> int | |
164 """ | |
165 Return path's uid. | |
166 | |
167 Does not follow symlinks: | |
168 https://github.com/pypa/pip/pull/935#discussion_r5307003 | |
169 | |
170 Placed this function in compat due to differences on AIX and | |
171 Jython, that should eventually go away. | |
172 | |
173 :raises OSError: When path is a symlink or can't be read. | |
174 """ | |
175 if hasattr(os, 'O_NOFOLLOW'): | |
176 fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) | |
177 file_uid = os.fstat(fd).st_uid | |
178 os.close(fd) | |
179 else: # AIX and Jython | |
180 # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW | |
181 if not os.path.islink(path): | |
182 # older versions of Jython don't have `os.fstat` | |
183 file_uid = os.stat(path).st_uid | |
184 else: | |
185 # raise OSError for parity with os.O_NOFOLLOW above | |
186 raise OSError( | |
187 "%s is a symlink; Will not return uid for symlinks" % path | |
188 ) | |
189 return file_uid | |
190 | |
191 | |
192 def expanduser(path): | |
193 # type: (str) -> str | |
194 """ | |
195 Expand ~ and ~user constructions. | |
196 | |
197 Includes a workaround for https://bugs.python.org/issue14768 | |
198 """ | |
199 expanded = os.path.expanduser(path) | |
200 if path.startswith('~/') and expanded.startswith('//'): | |
201 expanded = expanded[1:] | |
202 return expanded | |
203 | |
204 | |
205 # packages in the stdlib that may have installation metadata, but should not be | |
206 # considered 'installed'. this theoretically could be determined based on | |
207 # dist.location (py27:`sysconfig.get_paths()['stdlib']`, | |
208 # py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may | |
209 # make this ineffective, so hard-coding | |
210 stdlib_pkgs = {"python", "wsgiref", "argparse"} | |
211 | |
212 | |
213 # windows detection, covers cpython and ironpython | |
214 WINDOWS = (sys.platform.startswith("win") or | |
215 (sys.platform == 'cli' and os.name == 'nt')) | |
216 | |
217 | |
218 def samefile(file1, file2): | |
219 # type: (str, str) -> bool | |
220 """Provide an alternative for os.path.samefile on Windows/Python2""" | |
221 if hasattr(os.path, 'samefile'): | |
222 return os.path.samefile(file1, file2) | |
223 else: | |
224 path1 = os.path.normcase(os.path.abspath(file1)) | |
225 path2 = os.path.normcase(os.path.abspath(file2)) | |
226 return path1 == path2 | |
227 | |
228 | |
229 if hasattr(shutil, 'get_terminal_size'): | |
230 def get_terminal_size(): | |
231 # type: () -> Tuple[int, int] | |
232 """ | |
233 Returns a tuple (x, y) representing the width(x) and the height(y) | |
234 in characters of the terminal window. | |
235 """ | |
236 return tuple(shutil.get_terminal_size()) # type: ignore | |
237 else: | |
238 def get_terminal_size(): | |
239 # type: () -> Tuple[int, int] | |
240 """ | |
241 Returns a tuple (x, y) representing the width(x) and the height(y) | |
242 in characters of the terminal window. | |
243 """ | |
244 def ioctl_GWINSZ(fd): | |
245 try: | |
246 import fcntl | |
247 import termios | |
248 import struct | |
249 cr = struct.unpack_from( | |
250 'hh', | |
251 fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') | |
252 ) | |
253 except Exception: | |
254 return None | |
255 if cr == (0, 0): | |
256 return None | |
257 return cr | |
258 cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) | |
259 if not cr: | |
260 if sys.platform != "win32": | |
261 try: | |
262 fd = os.open(os.ctermid(), os.O_RDONLY) | |
263 cr = ioctl_GWINSZ(fd) | |
264 os.close(fd) | |
265 except Exception: | |
266 pass | |
267 if not cr: | |
268 cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) | |
269 return int(cr[1]), int(cr[0]) |