summaryrefslogtreecommitdiffhomepage
path: root/libs/requests
diff options
context:
space:
mode:
authormorpheus65535 <[email protected]>2022-01-23 23:07:52 -0500
committermorpheus65535 <[email protected]>2022-01-23 23:07:52 -0500
commit0c3c5a02a75bc61b6bf6e303de20e11741d2afac (patch)
tree30ae1d524ffe5d54172b7a4a8445d90c3461e659 /libs/requests
parent36bf0d219d0432c20e6314e0ce752b36f4d88e3c (diff)
downloadbazarr-0c3c5a02a75bc61b6bf6e303de20e11741d2afac.tar.gz
bazarr-0c3c5a02a75bc61b6bf6e303de20e11741d2afac.zip
Upgraded vendored Python dependencies to the latest versions and removed the unused dependencies.v1.0.3-beta.16
Diffstat (limited to 'libs/requests')
-rw-r--r--libs/requests/__init__.py39
-rw-r--r--libs/requests/__version__.py6
-rw-r--r--libs/requests/adapters.py9
-rw-r--r--libs/requests/api.py2
-rw-r--r--libs/requests/compat.py13
-rw-r--r--libs/requests/exceptions.py14
-rw-r--r--libs/requests/help.py20
-rw-r--r--libs/requests/models.py37
-rw-r--r--libs/requests/packages.py14
-rw-r--r--libs/requests/sessions.py24
-rw-r--r--libs/requests/utils.py92
11 files changed, 205 insertions, 65 deletions
diff --git a/libs/requests/__init__.py b/libs/requests/__init__.py
index f8f94295f..53a5b42af 100644
--- a/libs/requests/__init__.py
+++ b/libs/requests/__init__.py
@@ -41,12 +41,20 @@ is at <https://requests.readthedocs.io>.
"""
import urllib3
-import chardet
import warnings
from .exceptions import RequestsDependencyWarning
+try:
+ from charset_normalizer import __version__ as charset_normalizer_version
+except ImportError:
+ charset_normalizer_version = None
-def check_compatibility(urllib3_version, chardet_version):
+try:
+ from chardet import __version__ as chardet_version
+except ImportError:
+ chardet_version = None
+
+def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version):
urllib3_version = urllib3_version.split('.')
assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git.
@@ -62,12 +70,19 @@ def check_compatibility(urllib3_version, chardet_version):
assert minor >= 21
assert minor <= 26
- # Check chardet for compatibility.
- major, minor, patch = chardet_version.split('.')[:3]
- major, minor, patch = int(major), int(minor), int(patch)
- # chardet >= 3.0.2, < 5.0.0
- assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0)
-
+ # Check charset_normalizer for compatibility.
+ if chardet_version:
+ major, minor, patch = chardet_version.split('.')[:3]
+ major, minor, patch = int(major), int(minor), int(patch)
+ # chardet_version >= 3.0.2, < 5.0.0
+ assert (3, 0, 2) <= (major, minor, patch) < (5, 0, 0)
+ elif charset_normalizer_version:
+ major, minor, patch = charset_normalizer_version.split('.')[:3]
+ major, minor, patch = int(major), int(minor), int(patch)
+ # charset_normalizer >= 2.0.0 < 3.0.0
+ assert (2, 0, 0) <= (major, minor, patch) < (3, 0, 0)
+ else:
+ raise Exception("You need either charset_normalizer or chardet installed")
def _check_cryptography(cryptography_version):
# cryptography < 1.3.4
@@ -82,10 +97,10 @@ def _check_cryptography(cryptography_version):
# Check imported dependencies for compatibility.
try:
- check_compatibility(urllib3.__version__, chardet.__version__)
+ check_compatibility(urllib3.__version__, chardet_version, charset_normalizer_version)
except (AssertionError, ValueError):
- warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported "
- "version!".format(urllib3.__version__, chardet.__version__),
+ warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
+ "version!".format(urllib3.__version__, chardet_version, charset_normalizer_version),
RequestsDependencyWarning)
# Attempt to enable urllib3's fallback for SNI support
@@ -124,7 +139,7 @@ from .status_codes import codes
from .exceptions import (
RequestException, Timeout, URLRequired,
TooManyRedirects, HTTPError, ConnectionError,
- FileModeWarning, ConnectTimeout, ReadTimeout
+ FileModeWarning, ConnectTimeout, ReadTimeout, JSONDecodeError
)
# Set default logging handler to avoid "No handler found" warnings.
diff --git a/libs/requests/__version__.py b/libs/requests/__version__.py
index 1267488d2..e973b03b5 100644
--- a/libs/requests/__version__.py
+++ b/libs/requests/__version__.py
@@ -5,10 +5,10 @@
__title__ = 'requests'
__description__ = 'Python HTTP for Humans.'
__url__ = 'https://requests.readthedocs.io'
-__version__ = '2.25.1'
-__build__ = 0x022501
+__version__ = '2.27.1'
+__build__ = 0x022701
__author__ = 'Kenneth Reitz'
__author_email__ = '[email protected]'
__license__ = 'Apache 2.0'
-__copyright__ = 'Copyright 2020 Kenneth Reitz'
+__copyright__ = 'Copyright 2022 Kenneth Reitz'
__cake__ = u'\u2728 \U0001f370 \u2728'
diff --git a/libs/requests/adapters.py b/libs/requests/adapters.py
index fa4d9b3cc..fe22ff450 100644
--- a/libs/requests/adapters.py
+++ b/libs/requests/adapters.py
@@ -19,6 +19,7 @@ from urllib3.util.retry import Retry
from urllib3.exceptions import ClosedPoolError
from urllib3.exceptions import ConnectTimeoutError
from urllib3.exceptions import HTTPError as _HTTPError
+from urllib3.exceptions import InvalidHeader as _InvalidHeader
from urllib3.exceptions import MaxRetryError
from urllib3.exceptions import NewConnectionError
from urllib3.exceptions import ProxyError as _ProxyError
@@ -37,7 +38,7 @@ from .structures import CaseInsensitiveDict
from .cookies import extract_cookies_to_jar
from .exceptions import (ConnectionError, ConnectTimeout, ReadTimeout, SSLError,
ProxyError, RetryError, InvalidSchema, InvalidProxyURL,
- InvalidURL)
+ InvalidURL, InvalidHeader)
from .auth import _basic_auth_str
try:
@@ -457,9 +458,11 @@ class HTTPAdapter(BaseAdapter):
low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT)
try:
+ skip_host = 'Host' in request.headers
low_conn.putrequest(request.method,
url,
- skip_accept_encoding=True)
+ skip_accept_encoding=True,
+ skip_host=skip_host)
for header, value in request.headers.items():
low_conn.putheader(header, value)
@@ -527,6 +530,8 @@ class HTTPAdapter(BaseAdapter):
raise SSLError(e, request=request)
elif isinstance(e, ReadTimeoutError):
raise ReadTimeout(e, request=request)
+ elif isinstance(e, _InvalidHeader):
+ raise InvalidHeader(e, request=request)
else:
raise
diff --git a/libs/requests/api.py b/libs/requests/api.py
index e978e2031..4cba90eef 100644
--- a/libs/requests/api.py
+++ b/libs/requests/api.py
@@ -72,7 +72,6 @@ def get(url, params=None, **kwargs):
:rtype: requests.Response
"""
- kwargs.setdefault('allow_redirects', True)
return request('get', url, params=params, **kwargs)
@@ -85,7 +84,6 @@ def options(url, **kwargs):
:rtype: requests.Response
"""
- kwargs.setdefault('allow_redirects', True)
return request('options', url, **kwargs)
diff --git a/libs/requests/compat.py b/libs/requests/compat.py
index 5de0769f5..029ae62ac 100644
--- a/libs/requests/compat.py
+++ b/libs/requests/compat.py
@@ -8,7 +8,10 @@ This module handles import compatibility issues between Python 2 and
Python 3.
"""
-import chardet
+try:
+ import chardet
+except ImportError:
+ import charset_normalizer as chardet
import sys
@@ -25,8 +28,10 @@ is_py2 = (_ver[0] == 2)
#: Python 3.x?
is_py3 = (_ver[0] == 3)
+has_simplejson = False
try:
import simplejson as json
+ has_simplejson = True
except ImportError:
import json
@@ -46,13 +51,13 @@ if is_py2:
# Keep OrderedDict for backwards compatibility.
from collections import Callable, Mapping, MutableMapping, OrderedDict
-
builtin_str = str
bytes = str
str = unicode
basestring = basestring
numeric_types = (int, long, float)
integer_types = (int, long)
+ JSONDecodeError = ValueError
elif is_py3:
from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag
@@ -63,6 +68,10 @@ elif is_py3:
# Keep OrderedDict for backwards compatibility.
from collections import OrderedDict
from collections.abc import Callable, Mapping, MutableMapping
+ if has_simplejson:
+ from simplejson import JSONDecodeError
+ else:
+ from json import JSONDecodeError
builtin_str = str
str = str
diff --git a/libs/requests/exceptions.py b/libs/requests/exceptions.py
index 0e9c820c8..79697635a 100644
--- a/libs/requests/exceptions.py
+++ b/libs/requests/exceptions.py
@@ -8,6 +8,8 @@ This module contains the set of Requests' exceptions.
"""
from urllib3.exceptions import HTTPError as BaseHTTPError
+from .compat import JSONDecodeError as CompatJSONDecodeError
+
class RequestException(IOError):
"""There was an ambiguous exception that occurred while handling your
@@ -25,6 +27,14 @@ class RequestException(IOError):
super(RequestException, self).__init__(*args, **kwargs)
+class InvalidJSONError(RequestException):
+ """A JSON error occurred."""
+
+
+class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
+ """Couldn't decode the text into json"""
+
+
class HTTPError(RequestException):
"""An HTTP error occurred."""
@@ -70,11 +80,11 @@ class TooManyRedirects(RequestException):
class MissingSchema(RequestException, ValueError):
- """The URL schema (e.g. http or https) is missing."""
+ """The URL scheme (e.g. http or https) is missing."""
class InvalidSchema(RequestException, ValueError):
- """See defaults.py for valid schemas."""
+ """The URL scheme provided is either invalid or unsupported."""
class InvalidURL(RequestException, ValueError):
diff --git a/libs/requests/help.py b/libs/requests/help.py
index e53d35ef6..4cd6389f5 100644
--- a/libs/requests/help.py
+++ b/libs/requests/help.py
@@ -8,11 +8,20 @@ import ssl
import idna
import urllib3
-import chardet
from . import __version__ as requests_version
try:
+ import charset_normalizer
+except ImportError:
+ charset_normalizer = None
+
+try:
+ import chardet
+except ImportError:
+ chardet = None
+
+try:
from urllib3.contrib import pyopenssl
except ImportError:
pyopenssl = None
@@ -71,7 +80,12 @@ def info():
implementation_info = _implementation()
urllib3_info = {'version': urllib3.__version__}
- chardet_info = {'version': chardet.__version__}
+ charset_normalizer_info = {'version': None}
+ chardet_info = {'version': None}
+ if charset_normalizer:
+ charset_normalizer_info = {'version': charset_normalizer.__version__}
+ if chardet:
+ chardet_info = {'version': chardet.__version__}
pyopenssl_info = {
'version': None,
@@ -99,9 +113,11 @@ def info():
'implementation': implementation_info,
'system_ssl': system_ssl_info,
'using_pyopenssl': pyopenssl is not None,
+ 'using_charset_normalizer': chardet is None,
'pyOpenSSL': pyopenssl_info,
'urllib3': urllib3_info,
'chardet': chardet_info,
+ 'charset_normalizer': charset_normalizer_info,
'cryptography': cryptography_info,
'idna': idna_info,
'requests': {
diff --git a/libs/requests/models.py b/libs/requests/models.py
index ec2edc20b..dfbea854f 100644
--- a/libs/requests/models.py
+++ b/libs/requests/models.py
@@ -29,7 +29,9 @@ from .auth import HTTPBasicAuth
from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar
from .exceptions import (
HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError,
- ContentDecodingError, ConnectionError, StreamConsumedError)
+ ContentDecodingError, ConnectionError, StreamConsumedError,
+ InvalidJSONError)
+from .exceptions import JSONDecodeError as RequestsJSONDecodeError
from ._internal_utils import to_native_string, unicode_is_ascii
from .utils import (
guess_filename, get_auth_from_url, requote_uri,
@@ -38,7 +40,7 @@ from .utils import (
from .compat import (
Callable, Mapping,
cookielib, urlunparse, urlsplit, urlencode, str, bytes,
- is_py2, chardet, builtin_str, basestring)
+ is_py2, chardet, builtin_str, basestring, JSONDecodeError)
from .compat import json as complexjson
from .status_codes import codes
@@ -384,7 +386,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
raise InvalidURL(*e.args)
if not scheme:
- error = ("Invalid URL {0!r}: No schema supplied. Perhaps you meant http://{0}?")
+ error = ("Invalid URL {0!r}: No scheme supplied. Perhaps you meant http://{0}?")
error = error.format(to_native_string(url, 'utf8'))
raise MissingSchema(error)
@@ -401,7 +403,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
host = self._get_idna_encoded_host(host)
except UnicodeError:
raise InvalidURL('URL has an invalid label.')
- elif host.startswith(u'*'):
+ elif host.startswith((u'*', u'.')):
raise InvalidURL('URL has an invalid label.')
# Carefully reconstruct the network location
@@ -466,7 +468,12 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
# urllib3 requires a bytes-like body. Python 2's json.dumps
# provides this natively, but Python 3 gives a Unicode string.
content_type = 'application/json'
- body = complexjson.dumps(json)
+
+ try:
+ body = complexjson.dumps(json, allow_nan=False)
+ except ValueError as ve:
+ raise InvalidJSONError(ve, request=self)
+
if not isinstance(body, bytes):
body = body.encode('utf-8')
@@ -726,7 +733,7 @@ class Response(object):
@property
def apparent_encoding(self):
- """The apparent encoding, provided by the chardet library."""
+ """The apparent encoding, provided by the charset_normalizer or chardet libraries."""
return chardet.detect(self.content)['encoding']
def iter_content(self, chunk_size=1, decode_unicode=False):
@@ -840,7 +847,7 @@ class Response(object):
"""Content of the response, in unicode.
If Response.encoding is None, encoding will be guessed using
- ``chardet``.
+ ``charset_normalizer`` or ``chardet``.
The encoding of the response content is determined based solely on HTTP
headers, following RFC 2616 to the letter. If you can take advantage of
@@ -877,13 +884,14 @@ class Response(object):
r"""Returns the json-encoded content of a response, if any.
:param \*\*kwargs: Optional arguments that ``json.loads`` takes.
- :raises ValueError: If the response body does not contain valid json.
+ :raises requests.exceptions.JSONDecodeError: If the response body does not
+ contain valid json.
"""
if not self.encoding and self.content and len(self.content) > 3:
# No encoding set. JSON RFC 4627 section 3 states we should expect
# UTF-8, -16 or -32. Detect which one to use; If the detection or
- # decoding fails, fall back to `self.text` (using chardet to make
+ # decoding fails, fall back to `self.text` (using charset_normalizer to make
# a best guess).
encoding = guess_json_utf(self.content)
if encoding is not None:
@@ -897,7 +905,16 @@ class Response(object):
# and the server didn't bother to tell us what codec *was*
# used.
pass
- return complexjson.loads(self.text, **kwargs)
+
+ try:
+ return complexjson.loads(self.text, **kwargs)
+ except JSONDecodeError as e:
+ # Catch JSON-related errors and raise as requests.JSONDecodeError
+ # This aliases json.JSONDecodeError and simplejson.JSONDecodeError
+ if is_py2: # e is a ValueError
+ raise RequestsJSONDecodeError(e.message)
+ else:
+ raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
@property
def links(self):
diff --git a/libs/requests/packages.py b/libs/requests/packages.py
index 7232fe0ff..00196bff2 100644
--- a/libs/requests/packages.py
+++ b/libs/requests/packages.py
@@ -1,9 +1,17 @@
import sys
+try:
+ import chardet
+except ImportError:
+ import charset_normalizer as chardet
+ import warnings
+
+ warnings.filterwarnings('ignore', 'Trying to detect', module='charset_normalizer')
+
# This code exists for backwards compatibility reasons.
# I don't like it either. Just look the other way. :)
-for package in ('urllib3', 'idna', 'chardet'):
+for package in ('urllib3', 'idna'):
locals()[package] = __import__(package)
# This traversal is apparently necessary such that the identities are
# preserved (requests.packages.urllib3.* is urllib3.*)
@@ -11,4 +19,8 @@ for package in ('urllib3', 'idna', 'chardet'):
if mod == package or mod.startswith(package + '.'):
sys.modules['requests.packages.' + mod] = sys.modules[mod]
+target = chardet.__name__
+for mod in list(sys.modules):
+ if mod == target or mod.startswith(target + '.'):
+ sys.modules['requests.packages.' + target.replace(target, 'chardet')] = sys.modules[mod]
# Kinda cool, though, right?
diff --git a/libs/requests/sessions.py b/libs/requests/sessions.py
index 45ab8a5d3..3f59cab92 100644
--- a/libs/requests/sessions.py
+++ b/libs/requests/sessions.py
@@ -29,7 +29,7 @@ from .adapters import HTTPAdapter
from .utils import (
requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies,
- get_auth_from_url, rewind_body
+ get_auth_from_url, rewind_body, resolve_proxies
)
from .status_codes import codes
@@ -269,7 +269,6 @@ class SessionRedirectMixin(object):
if new_auth is not None:
prepared_request.prepare_auth(new_auth)
-
def rebuild_proxies(self, prepared_request, proxies):
"""This method re-evaluates the proxy configuration by considering the
environment variables. If we are redirected to a URL covered by
@@ -282,21 +281,9 @@ class SessionRedirectMixin(object):
:rtype: dict
"""
- proxies = proxies if proxies is not None else {}
headers = prepared_request.headers
- url = prepared_request.url
- scheme = urlparse(url).scheme
- new_proxies = proxies.copy()
- no_proxy = proxies.get('no_proxy')
-
- bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy)
- if self.trust_env and not bypass_proxy:
- environ_proxies = get_environ_proxies(url, no_proxy=no_proxy)
-
- proxy = environ_proxies.get(scheme, environ_proxies.get('all'))
-
- if proxy:
- new_proxies.setdefault(scheme, proxy)
+ scheme = urlparse(prepared_request.url).scheme
+ new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
if 'Proxy-Authorization' in headers:
del headers['Proxy-Authorization']
@@ -633,7 +620,10 @@ class Session(SessionRedirectMixin):
kwargs.setdefault('stream', self.stream)
kwargs.setdefault('verify', self.verify)
kwargs.setdefault('cert', self.cert)
- kwargs.setdefault('proxies', self.proxies)
+ if 'proxies' not in kwargs:
+ kwargs['proxies'] = resolve_proxies(
+ request, self.proxies, self.trust_env
+ )
# It's possible that users might accidentally send a Request object.
# Guard against that specific failure case.
diff --git a/libs/requests/utils.py b/libs/requests/utils.py
index db67938e6..153776c7f 100644
--- a/libs/requests/utils.py
+++ b/libs/requests/utils.py
@@ -20,6 +20,8 @@ import tempfile
import warnings
import zipfile
from collections import OrderedDict
+from urllib3.util import make_headers
+from urllib3.util import parse_url
from .__version__ import __version__
from . import certs
@@ -41,6 +43,11 @@ DEFAULT_CA_BUNDLE_PATH = certs.where()
DEFAULT_PORTS = {'http': 80, 'https': 443}
+# Ensure that ', ' is used to preserve previous delimiter behavior.
+DEFAULT_ACCEPT_ENCODING = ", ".join(
+ re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"])
+)
+
if sys.platform == 'win32':
# provide a proxy_bypass version on Windows without DNS lookups
@@ -118,7 +125,10 @@ def super_len(o):
elif hasattr(o, 'fileno'):
try:
fileno = o.fileno()
- except io.UnsupportedOperation:
+ except (io.UnsupportedOperation, AttributeError):
+ # AttributeError is a surprising exception, seeing as how we've just checked
+ # that `hasattr(o, 'fileno')`. It happens for objects obtained via
+ # `Tarfile.extractfile()`, per issue 5229.
pass
else:
total_length = os.fstat(fileno).st_size
@@ -148,7 +158,7 @@ def super_len(o):
current_position = total_length
else:
if hasattr(o, 'seek') and total_length is None:
- # StringIO and BytesIO have seek but no useable fileno
+ # StringIO and BytesIO have seek but no usable fileno
try:
# seek to end of file
o.seek(0, 2)
@@ -245,6 +255,10 @@ def extract_zipped_paths(path):
archive, member = os.path.split(path)
while archive and not os.path.exists(archive):
archive, prefix = os.path.split(archive)
+ if not prefix:
+ # If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split),
+ # we _can_ end up in an infinite loop on a rare corner case affecting a small number of users
+ break
member = '/'.join([prefix, member])
if not zipfile.is_zipfile(archive):
@@ -256,13 +270,28 @@ def extract_zipped_paths(path):
# we have a valid zip archive and a valid member of that archive
tmp = tempfile.gettempdir()
- extracted_path = os.path.join(tmp, *member.split('/'))
+ extracted_path = os.path.join(tmp, member.split('/')[-1])
if not os.path.exists(extracted_path):
- extracted_path = zip_file.extract(member, path=tmp)
-
+ # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition
+ with atomic_open(extracted_path) as file_handler:
+ file_handler.write(zip_file.read(member))
return extracted_path
+def atomic_open(filename):
+ """Write a file to the disk in an atomic fashion"""
+ replacer = os.rename if sys.version_info[0] == 2 else os.replace
+ tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename))
+ try:
+ with os.fdopen(tmp_descriptor, 'wb') as tmp_handler:
+ yield tmp_handler
+ replacer(tmp_name, filename)
+ except BaseException:
+ os.remove(tmp_name)
+ raise
+
+
def from_key_val_list(value):
"""Take an object and test to see if it can be represented as a
dictionary. Unless it can not be represented as such, return an
@@ -805,6 +834,33 @@ def select_proxy(url, proxies):
return proxy
+def resolve_proxies(request, proxies, trust_env=True):
+ """This method takes proxy information from a request and configuration
+ input to resolve a mapping of target proxies. This will consider settings
+ such a NO_PROXY to strip proxy configurations.
+
+ :param request: Request or PreparedRequest
+ :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs
+ :param trust_env: Boolean declaring whether to trust environment configs
+
+ :rtype: dict
+ """
+ proxies = proxies if proxies is not None else {}
+ url = request.url
+ scheme = urlparse(url).scheme
+ no_proxy = proxies.get('no_proxy')
+ new_proxies = proxies.copy()
+
+ if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy):
+ environ_proxies = get_environ_proxies(url, no_proxy=no_proxy)
+
+ proxy = environ_proxies.get(scheme, environ_proxies.get('all'))
+
+ if proxy:
+ new_proxies.setdefault(scheme, proxy)
+ return new_proxies
+
+
def default_user_agent(name="python-requests"):
"""
Return a string representing the default user agent.
@@ -820,7 +876,7 @@ def default_headers():
"""
return CaseInsensitiveDict({
'User-Agent': default_user_agent(),
- 'Accept-Encoding': ', '.join(('gzip', 'deflate')),
+ 'Accept-Encoding': DEFAULT_ACCEPT_ENCODING,
'Accept': '*/*',
'Connection': 'keep-alive',
})
@@ -907,15 +963,27 @@ def prepend_scheme_if_needed(url, new_scheme):
:rtype: str
"""
- scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme)
-
- # urlparse is a finicky beast, and sometimes decides that there isn't a
- # netloc present. Assume that it's being over-cautious, and switch netloc
- # and path if urlparse decided there was no netloc.
+ parsed = parse_url(url)
+ scheme, auth, host, port, path, query, fragment = parsed
+
+ # A defect in urlparse determines that there isn't a netloc present in some
+ # urls. We previously assumed parsing was overly cautious, and swapped the
+ # netloc and path. Due to a lack of tests on the original defect, this is
+ # maintained with parse_url for backwards compatibility.
+ netloc = parsed.netloc
if not netloc:
netloc, path = path, netloc
- return urlunparse((scheme, netloc, path, params, query, fragment))
+ if auth:
+ # parse_url doesn't provide the netloc with auth
+ # so we'll add it ourselves.
+ netloc = '@'.join([auth, netloc])
+ if scheme is None:
+ scheme = new_scheme
+ if path is None:
+ path = ''
+
+ return urlunparse((scheme, netloc, path, '', query, fragment))
def get_auth_from_url(url):