Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/botocore/compat.py: 49%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# http://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
14import copy
15import datetime
16import sys
17import inspect
18import warnings
19import hashlib
20from http.client import HTTPMessage
21import logging
22import shlex
23import re
24import os
25from collections import OrderedDict
26from collections.abc import MutableMapping
27from math import floor
29from botocore.vendored import six
30from botocore.exceptions import MD5UnavailableError
31from dateutil.tz import tzlocal
32from urllib3 import exceptions
34logger = logging.getLogger(__name__)
37class HTTPHeaders(HTTPMessage):
38 pass
40from urllib.parse import (
41 quote,
42 urlencode,
43 unquote,
44 unquote_plus,
45 urlparse,
46 urlsplit,
47 urlunsplit,
48 urljoin,
49 parse_qsl,
50 parse_qs,
51)
52from http.client import HTTPResponse
53from io import IOBase as _IOBase
54from base64 import encodebytes
55from email.utils import formatdate
56from itertools import zip_longest
57file_type = _IOBase
58zip = zip
60# In python3, unquote takes a str() object, url decodes it,
61# then takes the bytestring and decodes it to utf-8.
62unquote_str = unquote_plus
64def set_socket_timeout(http_response, timeout):
65 """Set the timeout of the socket from an HTTPResponse.
67 :param http_response: An instance of ``httplib.HTTPResponse``
69 """
70 http_response._fp.fp.raw._sock.settimeout(timeout)
72def accepts_kwargs(func):
73 # In python3.4.1, there's backwards incompatible
74 # changes when using getargspec with functools.partials.
75 return inspect.getfullargspec(func)[2]
77def ensure_unicode(s, encoding=None, errors=None):
78 # NOOP in Python 3, because every string is already unicode
79 return s
81def ensure_bytes(s, encoding='utf-8', errors='strict'):
82 if isinstance(s, str):
83 return s.encode(encoding, errors)
84 if isinstance(s, bytes):
85 return s
86 raise ValueError(f"Expected str or bytes, received {type(s)}.")
89try:
90 import xml.etree.cElementTree as ETree
91except ImportError:
92 # cElementTree does not exist from Python3.9+
93 import xml.etree.ElementTree as ETree
94XMLParseError = ETree.ParseError
95import json
98def filter_ssl_warnings():
99 # Ignore warnings related to SNI as it is not being used in validations.
100 warnings.filterwarnings(
101 'ignore',
102 message="A true SSLContext object is not available.*",
103 category=exceptions.InsecurePlatformWarning,
104 module=r".*urllib3\.util\.ssl_",
105 )
108@classmethod
109def from_dict(cls, d):
110 new_instance = cls()
111 for key, value in d.items():
112 new_instance[key] = value
113 return new_instance
116@classmethod
117def from_pairs(cls, pairs):
118 new_instance = cls()
119 for key, value in pairs:
120 new_instance[key] = value
121 return new_instance
124HTTPHeaders.from_dict = from_dict
125HTTPHeaders.from_pairs = from_pairs
128def copy_kwargs(kwargs):
129 """
130 This used to be a compat shim for 2.6 but is now just an alias.
131 """
132 copy_kwargs = copy.copy(kwargs)
133 return copy_kwargs
136def total_seconds(delta):
137 """
138 Returns the total seconds in a ``datetime.timedelta``.
140 This used to be a compat shim for 2.6 but is now just an alias.
142 :param delta: The timedelta object
143 :type delta: ``datetime.timedelta``
144 """
145 return delta.total_seconds()
148# Checks to see if md5 is available on this system. A given system might not
149# have access to it for various reasons, such as FIPS mode being enabled.
150try:
151 hashlib.md5()
152 MD5_AVAILABLE = True
153except ValueError:
154 MD5_AVAILABLE = False
157def get_md5(*args, **kwargs):
158 """
159 Attempts to get an md5 hashing object.
161 :param args: Args to pass to the MD5 constructor
162 :param kwargs: Key word arguments to pass to the MD5 constructor
163 :return: An MD5 hashing object if available. If it is unavailable, None
164 is returned if raise_error_if_unavailable is set to False.
165 """
166 if MD5_AVAILABLE:
167 return hashlib.md5(*args, **kwargs)
168 else:
169 raise MD5UnavailableError()
172def compat_shell_split(s, platform=None):
173 if platform is None:
174 platform = sys.platform
176 if platform == "win32":
177 return _windows_shell_split(s)
178 else:
179 return shlex.split(s)
182def _windows_shell_split(s):
183 """Splits up a windows command as the built-in command parser would.
185 Windows has potentially bizarre rules depending on where you look. When
186 spawning a process via the Windows C runtime (which is what python does
187 when you call popen) the rules are as follows:
189 https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
191 To summarize:
193 * Only space and tab are valid delimiters
194 * Double quotes are the only valid quotes
195 * Backslash is interpreted literally unless it is part of a chain that
196 leads up to a double quote. Then the backslashes escape the backslashes,
197 and if there is an odd number the final backslash escapes the quote.
199 :param s: The command string to split up into parts.
200 :return: A list of command components.
201 """
202 if not s:
203 return []
205 components = []
206 buff = []
207 is_quoted = False
208 num_backslashes = 0
209 for character in s:
210 if character == '\\':
211 # We can't simply append backslashes because we don't know if
212 # they are being used as escape characters or not. Instead we
213 # keep track of how many we've encountered and handle them when
214 # we encounter a different character.
215 num_backslashes += 1
216 elif character == '"':
217 if num_backslashes > 0:
218 # The backslashes are in a chain leading up to a double
219 # quote, so they are escaping each other.
220 buff.append('\\' * int(floor(num_backslashes / 2)))
221 remainder = num_backslashes % 2
222 num_backslashes = 0
223 if remainder == 1:
224 # The number of backslashes is uneven, so they are also
225 # escaping the double quote, so it needs to be added to
226 # the current component buffer.
227 buff.append('"')
228 continue
230 # We've encountered a double quote that is not escaped,
231 # so we toggle is_quoted.
232 is_quoted = not is_quoted
234 # If there are quotes, then we may want an empty string. To be
235 # safe, we add an empty string to the buffer so that we make
236 # sure it sticks around if there's nothing else between quotes.
237 # If there is other stuff between quotes, the empty string will
238 # disappear during the joining process.
239 buff.append('')
240 elif character in [' ', '\t'] and not is_quoted:
241 # Since the backslashes aren't leading up to a quote, we put in
242 # the exact number of backslashes.
243 if num_backslashes > 0:
244 buff.append('\\' * num_backslashes)
245 num_backslashes = 0
247 # Excess whitespace is ignored, so only add the components list
248 # if there is anything in the buffer.
249 if buff:
250 components.append(''.join(buff))
251 buff = []
252 else:
253 # Since the backslashes aren't leading up to a quote, we put in
254 # the exact number of backslashes.
255 if num_backslashes > 0:
256 buff.append('\\' * num_backslashes)
257 num_backslashes = 0
258 buff.append(character)
260 # Quotes must be terminated.
261 if is_quoted:
262 raise ValueError(f"No closing quotation in string: {s}")
264 # There may be some leftover backslashes, so we need to add them in.
265 # There's no quote so we add the exact number.
266 if num_backslashes > 0:
267 buff.append('\\' * num_backslashes)
269 # Add the final component in if there is anything in the buffer.
270 if buff:
271 components.append(''.join(buff))
273 return components
276def get_tzinfo_options():
277 # Due to dateutil/dateutil#197, Windows may fail to parse times in the past
278 # with the system clock. We can alternatively fallback to tzwininfo when
279 # this happens, which will get time info from the Windows registry.
280 if sys.platform == 'win32':
281 from dateutil.tz import tzwinlocal
283 return (tzlocal, tzwinlocal)
284 else:
285 return (tzlocal,)
288# Detect if CRT is available for use
289try:
290 import awscrt.auth
292 # Allow user opt-out if needed
293 disabled = os.environ.get('BOTO_DISABLE_CRT', "false")
294 HAS_CRT = not disabled.lower() == 'true'
295except ImportError:
296 HAS_CRT = False
299########################################################
300# urllib3 compat backports #
301########################################################
303# Vendoring IPv6 validation regex patterns from urllib3
304# https://github.com/urllib3/urllib3/blob/7e856c0/src/urllib3/util/url.py
305IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}"
306IPV4_RE = re.compile("^" + IPV4_PAT + "$")
307HEX_PAT = "[0-9A-Fa-f]{1,4}"
308LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=HEX_PAT, ipv4=IPV4_PAT)
309_subs = {"hex": HEX_PAT, "ls32": LS32_PAT}
310_variations = [
311 # 6( h16 ":" ) ls32
312 "(?:%(hex)s:){6}%(ls32)s",
313 # "::" 5( h16 ":" ) ls32
314 "::(?:%(hex)s:){5}%(ls32)s",
315 # [ h16 ] "::" 4( h16 ":" ) ls32
316 "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s",
317 # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
318 "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s",
319 # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
320 "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s",
321 # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
322 "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s",
323 # [ *4( h16 ":" ) h16 ] "::" ls32
324 "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s",
325 # [ *5( h16 ":" ) h16 ] "::" h16
326 "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s",
327 # [ *6( h16 ":" ) h16 ] "::"
328 "(?:(?:%(hex)s:){0,6}%(hex)s)?::",
329]
331UNRESERVED_PAT = (
332 r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._!\-~"
333)
334IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")"
335ZONE_ID_PAT = "(?:%25|%)(?:[" + UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+"
336IPV6_ADDRZ_PAT = r"\[" + IPV6_PAT + r"(?:" + ZONE_ID_PAT + r")?\]"
337IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT + "$")
339# These are the characters that are stripped by post-bpo-43882 urlparse().
340UNSAFE_URL_CHARS = frozenset('\t\r\n')
342# Detect if gzip is available for use
343try:
344 import gzip
345 HAS_GZIP = True
346except ImportError:
347 HAS_GZIP = False