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]) |
