Path: blob/master/venv/Lib/site-packages/requests/utils.py
811 views
# -*- coding: utf-8 -*-12"""3requests.utils4~~~~~~~~~~~~~~56This module provides utility functions that are used within Requests7that are also useful for external consumption.8"""910import codecs11import contextlib12import io13import os14import re15import socket16import struct17import sys18import tempfile19import warnings20import zipfile21from collections import OrderedDict2223from .__version__ import __version__24from . import certs25# to_native_string is unused here, but imported here for backwards compatibility26from ._internal_utils import to_native_string27from .compat import parse_http_list as _parse_list_header28from .compat import (29quote, urlparse, bytes, str, unquote, getproxies,30proxy_bypass, urlunparse, basestring, integer_types, is_py3,31proxy_bypass_environment, getproxies_environment, Mapping)32from .cookies import cookiejar_from_dict33from .structures import CaseInsensitiveDict34from .exceptions import (35InvalidURL, InvalidHeader, FileModeWarning, UnrewindableBodyError)3637NETRC_FILES = ('.netrc', '_netrc')3839DEFAULT_CA_BUNDLE_PATH = certs.where()4041DEFAULT_PORTS = {'http': 80, 'https': 443}424344if sys.platform == 'win32':45# provide a proxy_bypass version on Windows without DNS lookups4647def proxy_bypass_registry(host):48try:49if is_py3:50import winreg51else:52import _winreg as winreg53except ImportError:54return False5556try:57internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER,58r'Software\Microsoft\Windows\CurrentVersion\Internet Settings')59# ProxyEnable could be REG_SZ or REG_DWORD, normalizing it60proxyEnable = int(winreg.QueryValueEx(internetSettings,61'ProxyEnable')[0])62# ProxyOverride is almost always a string63proxyOverride = winreg.QueryValueEx(internetSettings,64'ProxyOverride')[0]65except OSError:66return False67if not proxyEnable or not proxyOverride:68return False6970# make a check value list from the registry entry: replace the71# '<local>' string by the localhost entry and the corresponding72# canonical entry.73proxyOverride = proxyOverride.split(';')74# now check if we match one of the registry values.75for test in proxyOverride:76if test == '<local>':77if '.' not in host:78return True79test = test.replace(".", r"\.") # mask dots80test = test.replace("*", r".*") # change glob sequence81test = test.replace("?", r".") # change glob char82if re.match(test, host, re.I):83return True84return False8586def proxy_bypass(host): # noqa87"""Return True, if the host should be bypassed.8889Checks proxy settings gathered from the environment, if specified,90or the registry.91"""92if getproxies_environment():93return proxy_bypass_environment(host)94else:95return proxy_bypass_registry(host)969798def dict_to_sequence(d):99"""Returns an internal sequence dictionary update."""100101if hasattr(d, 'items'):102d = d.items()103104return d105106107def super_len(o):108total_length = None109current_position = 0110111if hasattr(o, '__len__'):112total_length = len(o)113114elif hasattr(o, 'len'):115total_length = o.len116117elif hasattr(o, 'fileno'):118try:119fileno = o.fileno()120except io.UnsupportedOperation:121pass122else:123total_length = os.fstat(fileno).st_size124125# Having used fstat to determine the file length, we need to126# confirm that this file was opened up in binary mode.127if 'b' not in o.mode:128warnings.warn((129"Requests has determined the content-length for this "130"request using the binary size of the file: however, the "131"file has been opened in text mode (i.e. without the 'b' "132"flag in the mode). This may lead to an incorrect "133"content-length. In Requests 3.0, support will be removed "134"for files in text mode."),135FileModeWarning136)137138if hasattr(o, 'tell'):139try:140current_position = o.tell()141except (OSError, IOError):142# This can happen in some weird situations, such as when the file143# is actually a special file descriptor like stdin. In this144# instance, we don't know what the length is, so set it to zero and145# let requests chunk it instead.146if total_length is not None:147current_position = total_length148else:149if hasattr(o, 'seek') and total_length is None:150# StringIO and BytesIO have seek but no useable fileno151try:152# seek to end of file153o.seek(0, 2)154total_length = o.tell()155156# seek back to current position to support157# partially read file-like objects158o.seek(current_position or 0)159except (OSError, IOError):160total_length = 0161162if total_length is None:163total_length = 0164165return max(0, total_length - current_position)166167168def get_netrc_auth(url, raise_errors=False):169"""Returns the Requests tuple auth for a given url from netrc."""170171try:172from netrc import netrc, NetrcParseError173174netrc_path = None175176for f in NETRC_FILES:177try:178loc = os.path.expanduser('~/{}'.format(f))179except KeyError:180# os.path.expanduser can fail when $HOME is undefined and181# getpwuid fails. See https://bugs.python.org/issue20164 &182# https://github.com/psf/requests/issues/1846183return184185if os.path.exists(loc):186netrc_path = loc187break188189# Abort early if there isn't one.190if netrc_path is None:191return192193ri = urlparse(url)194195# Strip port numbers from netloc. This weird `if...encode`` dance is196# used for Python 3.2, which doesn't support unicode literals.197splitstr = b':'198if isinstance(url, str):199splitstr = splitstr.decode('ascii')200host = ri.netloc.split(splitstr)[0]201202try:203_netrc = netrc(netrc_path).authenticators(host)204if _netrc:205# Return with login / password206login_i = (0 if _netrc[0] else 1)207return (_netrc[login_i], _netrc[2])208except (NetrcParseError, IOError):209# If there was a parsing error or a permissions issue reading the file,210# we'll just skip netrc auth unless explicitly asked to raise errors.211if raise_errors:212raise213214# AppEngine hackiness.215except (ImportError, AttributeError):216pass217218219def guess_filename(obj):220"""Tries to guess the filename of the given object."""221name = getattr(obj, 'name', None)222if (name and isinstance(name, basestring) and name[0] != '<' and223name[-1] != '>'):224return os.path.basename(name)225226227def extract_zipped_paths(path):228"""Replace nonexistent paths that look like they refer to a member of a zip229archive with the location of an extracted copy of the target, or else230just return the provided path unchanged.231"""232if os.path.exists(path):233# this is already a valid path, no need to do anything further234return path235236# find the first valid part of the provided path and treat that as a zip archive237# assume the rest of the path is the name of a member in the archive238archive, member = os.path.split(path)239while archive and not os.path.exists(archive):240archive, prefix = os.path.split(archive)241member = '/'.join([prefix, member])242243if not zipfile.is_zipfile(archive):244return path245246zip_file = zipfile.ZipFile(archive)247if member not in zip_file.namelist():248return path249250# we have a valid zip archive and a valid member of that archive251tmp = tempfile.gettempdir()252extracted_path = os.path.join(tmp, *member.split('/'))253if not os.path.exists(extracted_path):254extracted_path = zip_file.extract(member, path=tmp)255256return extracted_path257258259def from_key_val_list(value):260"""Take an object and test to see if it can be represented as a261dictionary. Unless it can not be represented as such, return an262OrderedDict, e.g.,263264::265266>>> from_key_val_list([('key', 'val')])267OrderedDict([('key', 'val')])268>>> from_key_val_list('string')269Traceback (most recent call last):270...271ValueError: cannot encode objects that are not 2-tuples272>>> from_key_val_list({'key': 'val'})273OrderedDict([('key', 'val')])274275:rtype: OrderedDict276"""277if value is None:278return None279280if isinstance(value, (str, bytes, bool, int)):281raise ValueError('cannot encode objects that are not 2-tuples')282283return OrderedDict(value)284285286def to_key_val_list(value):287"""Take an object and test to see if it can be represented as a288dictionary. If it can be, return a list of tuples, e.g.,289290::291292>>> to_key_val_list([('key', 'val')])293[('key', 'val')]294>>> to_key_val_list({'key': 'val'})295[('key', 'val')]296>>> to_key_val_list('string')297Traceback (most recent call last):298...299ValueError: cannot encode objects that are not 2-tuples300301:rtype: list302"""303if value is None:304return None305306if isinstance(value, (str, bytes, bool, int)):307raise ValueError('cannot encode objects that are not 2-tuples')308309if isinstance(value, Mapping):310value = value.items()311312return list(value)313314315# From mitsuhiko/werkzeug (used with permission).316def parse_list_header(value):317"""Parse lists as described by RFC 2068 Section 2.318319In particular, parse comma-separated lists where the elements of320the list may include quoted-strings. A quoted-string could321contain a comma. A non-quoted string could have quotes in the322middle. Quotes are removed automatically after parsing.323324It basically works like :func:`parse_set_header` just that items325may appear multiple times and case sensitivity is preserved.326327The return value is a standard :class:`list`:328329>>> parse_list_header('token, "quoted value"')330['token', 'quoted value']331332To create a header from the :class:`list` again, use the333:func:`dump_header` function.334335:param value: a string with a list header.336:return: :class:`list`337:rtype: list338"""339result = []340for item in _parse_list_header(value):341if item[:1] == item[-1:] == '"':342item = unquote_header_value(item[1:-1])343result.append(item)344return result345346347# From mitsuhiko/werkzeug (used with permission).348def parse_dict_header(value):349"""Parse lists of key, value pairs as described by RFC 2068 Section 2 and350convert them into a python dict:351352>>> d = parse_dict_header('foo="is a fish", bar="as well"')353>>> type(d) is dict354True355>>> sorted(d.items())356[('bar', 'as well'), ('foo', 'is a fish')]357358If there is no value for a key it will be `None`:359360>>> parse_dict_header('key_without_value')361{'key_without_value': None}362363To create a header from the :class:`dict` again, use the364:func:`dump_header` function.365366:param value: a string with a dict header.367:return: :class:`dict`368:rtype: dict369"""370result = {}371for item in _parse_list_header(value):372if '=' not in item:373result[item] = None374continue375name, value = item.split('=', 1)376if value[:1] == value[-1:] == '"':377value = unquote_header_value(value[1:-1])378result[name] = value379return result380381382# From mitsuhiko/werkzeug (used with permission).383def unquote_header_value(value, is_filename=False):384r"""Unquotes a header value. (Reversal of :func:`quote_header_value`).385This does not use the real unquoting but what browsers are actually386using for quoting.387388:param value: the header value to unquote.389:rtype: str390"""391if value and value[0] == value[-1] == '"':392# this is not the real unquoting, but fixing this so that the393# RFC is met will result in bugs with internet explorer and394# probably some other browsers as well. IE for example is395# uploading files with "C:\foo\bar.txt" as filename396value = value[1:-1]397398# if this is a filename and the starting characters look like399# a UNC path, then just return the value without quotes. Using the400# replace sequence below on a UNC path has the effect of turning401# the leading double slash into a single slash and then402# _fix_ie_filename() doesn't work correctly. See #458.403if not is_filename or value[:2] != '\\\\':404return value.replace('\\\\', '\\').replace('\\"', '"')405return value406407408def dict_from_cookiejar(cj):409"""Returns a key/value dictionary from a CookieJar.410411:param cj: CookieJar object to extract cookies from.412:rtype: dict413"""414415cookie_dict = {}416417for cookie in cj:418cookie_dict[cookie.name] = cookie.value419420return cookie_dict421422423def add_dict_to_cookiejar(cj, cookie_dict):424"""Returns a CookieJar from a key/value dictionary.425426:param cj: CookieJar to insert cookies into.427:param cookie_dict: Dict of key/values to insert into CookieJar.428:rtype: CookieJar429"""430431return cookiejar_from_dict(cookie_dict, cj)432433434def get_encodings_from_content(content):435"""Returns encodings from given content string.436437:param content: bytestring to extract encodings from.438"""439warnings.warn((440'In requests 3.0, get_encodings_from_content will be removed. For '441'more information, please see the discussion on issue #2266. (This'442' warning should only appear once.)'),443DeprecationWarning)444445charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I)446pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I)447xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]')448449return (charset_re.findall(content) +450pragma_re.findall(content) +451xml_re.findall(content))452453454def _parse_content_type_header(header):455"""Returns content type and parameters from given header456457:param header: string458:return: tuple containing content type and dictionary of459parameters460"""461462tokens = header.split(';')463content_type, params = tokens[0].strip(), tokens[1:]464params_dict = {}465items_to_strip = "\"' "466467for param in params:468param = param.strip()469if param:470key, value = param, True471index_of_equals = param.find("=")472if index_of_equals != -1:473key = param[:index_of_equals].strip(items_to_strip)474value = param[index_of_equals + 1:].strip(items_to_strip)475params_dict[key.lower()] = value476return content_type, params_dict477478479def get_encoding_from_headers(headers):480"""Returns encodings from given HTTP Header Dict.481482:param headers: dictionary to extract encoding from.483:rtype: str484"""485486content_type = headers.get('content-type')487488if not content_type:489return None490491content_type, params = _parse_content_type_header(content_type)492493if 'charset' in params:494return params['charset'].strip("'\"")495496if 'text' in content_type:497return 'ISO-8859-1'498499500def stream_decode_response_unicode(iterator, r):501"""Stream decodes a iterator."""502503if r.encoding is None:504for item in iterator:505yield item506return507508decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace')509for chunk in iterator:510rv = decoder.decode(chunk)511if rv:512yield rv513rv = decoder.decode(b'', final=True)514if rv:515yield rv516517518def iter_slices(string, slice_length):519"""Iterate over slices of a string."""520pos = 0521if slice_length is None or slice_length <= 0:522slice_length = len(string)523while pos < len(string):524yield string[pos:pos + slice_length]525pos += slice_length526527528def get_unicode_from_response(r):529"""Returns the requested content back in unicode.530531:param r: Response object to get unicode content from.532533Tried:5345351. charset from content-type5362. fall back and replace all unicode characters537538:rtype: str539"""540warnings.warn((541'In requests 3.0, get_unicode_from_response will be removed. For '542'more information, please see the discussion on issue #2266. (This'543' warning should only appear once.)'),544DeprecationWarning)545546tried_encodings = []547548# Try charset from content-type549encoding = get_encoding_from_headers(r.headers)550551if encoding:552try:553return str(r.content, encoding)554except UnicodeError:555tried_encodings.append(encoding)556557# Fall back:558try:559return str(r.content, encoding, errors='replace')560except TypeError:561return r.content562563564# The unreserved URI characters (RFC 3986)565UNRESERVED_SET = frozenset(566"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~")567568569def unquote_unreserved(uri):570"""Un-escape any percent-escape sequences in a URI that are unreserved571characters. This leaves all reserved, illegal and non-ASCII bytes encoded.572573:rtype: str574"""575parts = uri.split('%')576for i in range(1, len(parts)):577h = parts[i][0:2]578if len(h) == 2 and h.isalnum():579try:580c = chr(int(h, 16))581except ValueError:582raise InvalidURL("Invalid percent-escape sequence: '%s'" % h)583584if c in UNRESERVED_SET:585parts[i] = c + parts[i][2:]586else:587parts[i] = '%' + parts[i]588else:589parts[i] = '%' + parts[i]590return ''.join(parts)591592593def requote_uri(uri):594"""Re-quote the given URI.595596This function passes the given URI through an unquote/quote cycle to597ensure that it is fully and consistently quoted.598599:rtype: str600"""601safe_with_percent = "!#$%&'()*+,/:;=?@[]~"602safe_without_percent = "!#$&'()*+,/:;=?@[]~"603try:604# Unquote only the unreserved characters605# Then quote only illegal characters (do not quote reserved,606# unreserved, or '%')607return quote(unquote_unreserved(uri), safe=safe_with_percent)608except InvalidURL:609# We couldn't unquote the given URI, so let's try quoting it, but610# there may be unquoted '%'s in the URI. We need to make sure they're611# properly quoted so they do not cause issues elsewhere.612return quote(uri, safe=safe_without_percent)613614615def address_in_network(ip, net):616"""This function allows you to check if an IP belongs to a network subnet617618Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24619returns False if ip = 192.168.1.1 and net = 192.168.100.0/24620621:rtype: bool622"""623ipaddr = struct.unpack('=L', socket.inet_aton(ip))[0]624netaddr, bits = net.split('/')625netmask = struct.unpack('=L', socket.inet_aton(dotted_netmask(int(bits))))[0]626network = struct.unpack('=L', socket.inet_aton(netaddr))[0] & netmask627return (ipaddr & netmask) == (network & netmask)628629630def dotted_netmask(mask):631"""Converts mask from /xx format to xxx.xxx.xxx.xxx632633Example: if mask is 24 function returns 255.255.255.0634635:rtype: str636"""637bits = 0xffffffff ^ (1 << 32 - mask) - 1638return socket.inet_ntoa(struct.pack('>I', bits))639640641def is_ipv4_address(string_ip):642"""643:rtype: bool644"""645try:646socket.inet_aton(string_ip)647except socket.error:648return False649return True650651652def is_valid_cidr(string_network):653"""654Very simple check of the cidr format in no_proxy variable.655656:rtype: bool657"""658if string_network.count('/') == 1:659try:660mask = int(string_network.split('/')[1])661except ValueError:662return False663664if mask < 1 or mask > 32:665return False666667try:668socket.inet_aton(string_network.split('/')[0])669except socket.error:670return False671else:672return False673return True674675676@contextlib.contextmanager677def set_environ(env_name, value):678"""Set the environment variable 'env_name' to 'value'679680Save previous value, yield, and then restore the previous value stored in681the environment variable 'env_name'.682683If 'value' is None, do nothing"""684value_changed = value is not None685if value_changed:686old_value = os.environ.get(env_name)687os.environ[env_name] = value688try:689yield690finally:691if value_changed:692if old_value is None:693del os.environ[env_name]694else:695os.environ[env_name] = old_value696697698def should_bypass_proxies(url, no_proxy):699"""700Returns whether we should bypass proxies or not.701702:rtype: bool703"""704# Prioritize lowercase environment variables over uppercase705# to keep a consistent behaviour with other http projects (curl, wget).706get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper())707708# First check whether no_proxy is defined. If it is, check that the URL709# we're getting isn't in the no_proxy list.710no_proxy_arg = no_proxy711if no_proxy is None:712no_proxy = get_proxy('no_proxy')713parsed = urlparse(url)714715if parsed.hostname is None:716# URLs don't always have hostnames, e.g. file:/// urls.717return True718719if no_proxy:720# We need to check whether we match here. We need to see if we match721# the end of the hostname, both with and without the port.722no_proxy = (723host for host in no_proxy.replace(' ', '').split(',') if host724)725726if is_ipv4_address(parsed.hostname):727for proxy_ip in no_proxy:728if is_valid_cidr(proxy_ip):729if address_in_network(parsed.hostname, proxy_ip):730return True731elif parsed.hostname == proxy_ip:732# If no_proxy ip was defined in plain IP notation instead of cidr notation &733# matches the IP of the index734return True735else:736host_with_port = parsed.hostname737if parsed.port:738host_with_port += ':{}'.format(parsed.port)739740for host in no_proxy:741if parsed.hostname.endswith(host) or host_with_port.endswith(host):742# The URL does match something in no_proxy, so we don't want743# to apply the proxies on this URL.744return True745746with set_environ('no_proxy', no_proxy_arg):747# parsed.hostname can be `None` in cases such as a file URI.748try:749bypass = proxy_bypass(parsed.hostname)750except (TypeError, socket.gaierror):751bypass = False752753if bypass:754return True755756return False757758759def get_environ_proxies(url, no_proxy=None):760"""761Return a dict of environment proxies.762763:rtype: dict764"""765if should_bypass_proxies(url, no_proxy=no_proxy):766return {}767else:768return getproxies()769770771def select_proxy(url, proxies):772"""Select a proxy for the url, if applicable.773774:param url: The url being for the request775:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs776"""777proxies = proxies or {}778urlparts = urlparse(url)779if urlparts.hostname is None:780return proxies.get(urlparts.scheme, proxies.get('all'))781782proxy_keys = [783urlparts.scheme + '://' + urlparts.hostname,784urlparts.scheme,785'all://' + urlparts.hostname,786'all',787]788proxy = None789for proxy_key in proxy_keys:790if proxy_key in proxies:791proxy = proxies[proxy_key]792break793794return proxy795796797def default_user_agent(name="python-requests"):798"""799Return a string representing the default user agent.800801:rtype: str802"""803return '%s/%s' % (name, __version__)804805806def default_headers():807"""808:rtype: requests.structures.CaseInsensitiveDict809"""810return CaseInsensitiveDict({811'User-Agent': default_user_agent(),812'Accept-Encoding': ', '.join(('gzip', 'deflate')),813'Accept': '*/*',814'Connection': 'keep-alive',815})816817818def parse_header_links(value):819"""Return a list of parsed link headers proxies.820821i.e. Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg",<http://.../back.jpeg>; rel=back;type="image/jpeg"822823:rtype: list824"""825826links = []827828replace_chars = ' \'"'829830value = value.strip(replace_chars)831if not value:832return links833834for val in re.split(', *<', value):835try:836url, params = val.split(';', 1)837except ValueError:838url, params = val, ''839840link = {'url': url.strip('<> \'"')}841842for param in params.split(';'):843try:844key, value = param.split('=')845except ValueError:846break847848link[key.strip(replace_chars)] = value.strip(replace_chars)849850links.append(link)851852return links853854855# Null bytes; no need to recreate these on each call to guess_json_utf856_null = '\x00'.encode('ascii') # encoding to ASCII for Python 3857_null2 = _null * 2858_null3 = _null * 3859860861def guess_json_utf(data):862"""863:rtype: str864"""865# JSON always starts with two ASCII characters, so detection is as866# easy as counting the nulls and from their location and count867# determine the encoding. Also detect a BOM, if present.868sample = data[:4]869if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE):870return 'utf-32' # BOM included871if sample[:3] == codecs.BOM_UTF8:872return 'utf-8-sig' # BOM included, MS style (discouraged)873if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):874return 'utf-16' # BOM included875nullcount = sample.count(_null)876if nullcount == 0:877return 'utf-8'878if nullcount == 2:879if sample[::2] == _null2: # 1st and 3rd are null880return 'utf-16-be'881if sample[1::2] == _null2: # 2nd and 4th are null882return 'utf-16-le'883# Did not detect 2 valid UTF-16 ascii-range characters884if nullcount == 3:885if sample[:3] == _null3:886return 'utf-32-be'887if sample[1:] == _null3:888return 'utf-32-le'889# Did not detect a valid UTF-32 ascii-range character890return None891892893def prepend_scheme_if_needed(url, new_scheme):894"""Given a URL that may or may not have a scheme, prepend the given scheme.895Does not replace a present scheme with the one provided as an argument.896897:rtype: str898"""899scheme, netloc, path, params, query, fragment = urlparse(url, new_scheme)900901# urlparse is a finicky beast, and sometimes decides that there isn't a902# netloc present. Assume that it's being over-cautious, and switch netloc903# and path if urlparse decided there was no netloc.904if not netloc:905netloc, path = path, netloc906907return urlunparse((scheme, netloc, path, params, query, fragment))908909910def get_auth_from_url(url):911"""Given a url with authentication components, extract them into a tuple of912username,password.913914:rtype: (str,str)915"""916parsed = urlparse(url)917918try:919auth = (unquote(parsed.username), unquote(parsed.password))920except (AttributeError, TypeError):921auth = ('', '')922923return auth924925926# Moved outside of function to avoid recompile every call927_CLEAN_HEADER_REGEX_BYTE = re.compile(b'^\\S[^\\r\\n]*$|^$')928_CLEAN_HEADER_REGEX_STR = re.compile(r'^\S[^\r\n]*$|^$')929930931def check_header_validity(header):932"""Verifies that header value is a string which doesn't contain933leading whitespace or return characters. This prevents unintended934header injection.935936:param header: tuple, in the format (name, value).937"""938name, value = header939940if isinstance(value, bytes):941pat = _CLEAN_HEADER_REGEX_BYTE942else:943pat = _CLEAN_HEADER_REGEX_STR944try:945if not pat.match(value):946raise InvalidHeader("Invalid return character or leading space in header: %s" % name)947except TypeError:948raise InvalidHeader("Value for header {%s: %s} must be of type str or "949"bytes, not %s" % (name, value, type(value)))950951952def urldefragauth(url):953"""954Given a url remove the fragment and the authentication part.955956:rtype: str957"""958scheme, netloc, path, params, query, fragment = urlparse(url)959960# see func:`prepend_scheme_if_needed`961if not netloc:962netloc, path = path, netloc963964netloc = netloc.rsplit('@', 1)[-1]965966return urlunparse((scheme, netloc, path, params, query, ''))967968969def rewind_body(prepared_request):970"""Move file pointer back to its recorded starting position971so it can be read again on redirect.972"""973body_seek = getattr(prepared_request.body, 'seek', None)974if body_seek is not None and isinstance(prepared_request._body_position, integer_types):975try:976body_seek(prepared_request._body_position)977except (IOError, OSError):978raise UnrewindableBodyError("An error occurred when rewinding request "979"body for redirect.")980else:981raise UnrewindableBodyError("Unable to rewind request body for redirect.")982983984