Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/yarl/_url.py
7771 views
import functools1import sys2import warnings3from collections.abc import Mapping, Sequence4from ipaddress import ip_address5from urllib.parse import SplitResult, parse_qsl, urljoin, urlsplit, urlunsplit, quote67from multidict import MultiDict, MultiDictProxy8import idna910import math111213from ._quoting import _Quoter, _Unquoter141516DEFAULT_PORTS = {"http": 80, "https": 443, "ws": 80, "wss": 443}1718sentinel = object()192021def rewrite_module(obj: object) -> object:22obj.__module__ = "yarl"23return obj242526class cached_property:27"""Use as a class method decorator. It operates almost exactly like28the Python `@property` decorator, but it puts the result of the29method it decorates into the instance dict after the first call,30effectively replacing the function it decorates with an instance31variable. It is, in Python parlance, a data descriptor.3233"""3435def __init__(self, wrapped):36self.wrapped = wrapped37try:38self.__doc__ = wrapped.__doc__39except AttributeError: # pragma: no cover40self.__doc__ = ""41self.name = wrapped.__name__4243def __get__(self, inst, owner, _sentinel=sentinel):44if inst is None:45return self46val = inst._cache.get(self.name, _sentinel)47if val is not _sentinel:48return val49val = self.wrapped(inst)50inst._cache[self.name] = val51return val5253def __set__(self, inst, value):54raise AttributeError("cached property is read-only")555657@rewrite_module58class URL:59# Don't derive from str60# follow pathlib.Path design61# probably URL will not suffer from pathlib problems:62# it's intended for libraries like aiohttp,63# not to be passed into standard library functions like os.open etc.6465# URL grammar (RFC 3986)66# pct-encoded = "%" HEXDIG HEXDIG67# reserved = gen-delims / sub-delims68# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"69# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"70# / "*" / "+" / "," / ";" / "="71# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"72# URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]73# hier-part = "//" authority path-abempty74# / path-absolute75# / path-rootless76# / path-empty77# scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )78# authority = [ userinfo "@" ] host [ ":" port ]79# userinfo = *( unreserved / pct-encoded / sub-delims / ":" )80# host = IP-literal / IPv4address / reg-name81# IP-literal = "[" ( IPv6address / IPvFuture ) "]"82# IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )83# IPv6address = 6( h16 ":" ) ls3284# / "::" 5( h16 ":" ) ls3285# / [ h16 ] "::" 4( h16 ":" ) ls3286# / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls3287# / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls3288# / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls3289# / [ *4( h16 ":" ) h16 ] "::" ls3290# / [ *5( h16 ":" ) h16 ] "::" h1691# / [ *6( h16 ":" ) h16 ] "::"92# ls32 = ( h16 ":" h16 ) / IPv4address93# ; least-significant 32 bits of address94# h16 = 1*4HEXDIG95# ; 16 bits of address represented in hexadecimal96# IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet97# dec-octet = DIGIT ; 0-998# / %x31-39 DIGIT ; 10-9999# / "1" 2DIGIT ; 100-199100# / "2" %x30-34 DIGIT ; 200-249101# / "25" %x30-35 ; 250-255102# reg-name = *( unreserved / pct-encoded / sub-delims )103# port = *DIGIT104# path = path-abempty ; begins with "/" or is empty105# / path-absolute ; begins with "/" but not "//"106# / path-noscheme ; begins with a non-colon segment107# / path-rootless ; begins with a segment108# / path-empty ; zero characters109# path-abempty = *( "/" segment )110# path-absolute = "/" [ segment-nz *( "/" segment ) ]111# path-noscheme = segment-nz-nc *( "/" segment )112# path-rootless = segment-nz *( "/" segment )113# path-empty = 0<pchar>114# segment = *pchar115# segment-nz = 1*pchar116# segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )117# ; non-zero-length segment without any colon ":"118# pchar = unreserved / pct-encoded / sub-delims / ":" / "@"119# query = *( pchar / "/" / "?" )120# fragment = *( pchar / "/" / "?" )121# URI-reference = URI / relative-ref122# relative-ref = relative-part [ "?" query ] [ "#" fragment ]123# relative-part = "//" authority path-abempty124# / path-absolute125# / path-noscheme126# / path-empty127# absolute-URI = scheme ":" hier-part [ "?" query ]128__slots__ = ("_cache", "_val")129130_QUOTER = _Quoter(requote=False)131_REQUOTER = _Quoter()132_PATH_QUOTER = _Quoter(safe="@:", protected="/+", requote=False)133_PATH_REQUOTER = _Quoter(safe="@:", protected="/+")134_QUERY_QUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True, requote=False)135_QUERY_REQUOTER = _Quoter(safe="?/:@", protected="=+&;", qs=True)136_QUERY_PART_QUOTER = _Quoter(safe="?/:@", qs=True, requote=False)137_FRAGMENT_QUOTER = _Quoter(safe="?/:@", requote=False)138_FRAGMENT_REQUOTER = _Quoter(safe="?/:@")139140_UNQUOTER = _Unquoter()141_PATH_UNQUOTER = _Unquoter(unsafe="+")142_QS_UNQUOTER = _Unquoter(qs=True)143144def __new__(cls, val="", *, encoded=False, strict=None):145if strict is not None: # pragma: no cover146warnings.warn("strict parameter is ignored")147if type(val) is cls:148return val149if type(val) is str:150val = urlsplit(val)151elif type(val) is SplitResult:152if not encoded:153raise ValueError("Cannot apply decoding to SplitResult")154elif isinstance(val, str):155val = urlsplit(str(val))156else:157raise TypeError("Constructor parameter should be str")158159if not encoded:160if not val[1]: # netloc161netloc = ""162host = ""163else:164host = val.hostname165if host is None:166raise ValueError("Invalid URL: host is required for absolute urls")167168try:169port = val.port170except ValueError as e:171raise ValueError(172"Invalid URL: port can't be converted to integer"173) from e174175netloc = cls._make_netloc(176val.username, val.password, host, port, encode=True, requote=True177)178path = cls._PATH_REQUOTER(val[2])179if netloc:180path = cls._normalize_path(path)181182cls._validate_authority_uri_abs_path(host=host, path=path)183query = cls._QUERY_REQUOTER(val[3])184fragment = cls._FRAGMENT_REQUOTER(val[4])185val = SplitResult(val[0], netloc, path, query, fragment)186187self = object.__new__(cls)188self._val = val189self._cache = {}190return self191192@classmethod193def build(194cls,195*,196scheme="",197authority="",198user=None,199password=None,200host="",201port=None,202path="",203query=None,204query_string="",205fragment="",206encoded=False207):208"""Creates and returns a new URL"""209210if authority and (user or password or host or port):211raise ValueError(212'Can\'t mix "authority" with "user", "password", "host" or "port".'213)214if port and not host:215raise ValueError('Can\'t build URL with "port" but without "host".')216if query and query_string:217raise ValueError('Only one of "query" or "query_string" should be passed')218if (219scheme is None220or authority is None221or path is None222or query_string is None223or fragment is None224):225raise TypeError(226'NoneType is illegal for "scheme", "authority", "path", '227'"query_string", and "fragment" args, use empty string instead.'228)229230if authority:231if encoded:232netloc = authority233else:234tmp = SplitResult("", authority, "", "", "")235netloc = cls._make_netloc(236tmp.username, tmp.password, tmp.hostname, tmp.port, encode=True237)238elif not user and not password and not host and not port:239netloc = ""240else:241netloc = cls._make_netloc(242user, password, host, port, encode=not encoded, encode_host=not encoded243)244if not encoded:245path = cls._PATH_QUOTER(path)246if netloc:247path = cls._normalize_path(path)248249cls._validate_authority_uri_abs_path(host=host, path=path)250query_string = cls._QUERY_QUOTER(query_string)251fragment = cls._FRAGMENT_QUOTER(fragment)252253url = cls(254SplitResult(scheme, netloc, path, query_string, fragment), encoded=True255)256257if query:258return url.with_query(query)259else:260return url261262def __init_subclass__(cls):263raise TypeError("Inheriting a class {!r} from URL is forbidden".format(cls))264265def __str__(self):266val = self._val267if not val.path and self.is_absolute() and (val.query or val.fragment):268val = val._replace(path="/")269return urlunsplit(val)270271def __repr__(self):272return "{}('{}')".format(self.__class__.__name__, str(self))273274def __bytes__(self):275return str(self).encode("ascii")276277def __eq__(self, other):278if not type(other) is URL:279return NotImplemented280281val1 = self._val282if not val1.path and self.is_absolute():283val1 = val1._replace(path="/")284285val2 = other._val286if not val2.path and other.is_absolute():287val2 = val2._replace(path="/")288289return val1 == val2290291def __hash__(self):292ret = self._cache.get("hash")293if ret is None:294val = self._val295if not val.path and self.is_absolute():296val = val._replace(path="/")297ret = self._cache["hash"] = hash(val)298return ret299300def __le__(self, other):301if not type(other) is URL:302return NotImplemented303return self._val <= other._val304305def __lt__(self, other):306if not type(other) is URL:307return NotImplemented308return self._val < other._val309310def __ge__(self, other):311if not type(other) is URL:312return NotImplemented313return self._val >= other._val314315def __gt__(self, other):316if not type(other) is URL:317return NotImplemented318return self._val > other._val319320def __truediv__(self, name):321name = self._PATH_QUOTER(name)322if name.startswith("/"):323raise ValueError(324"Appending path {!r} starting from slash is forbidden".format(name)325)326path = self._val.path327if path == "/":328new_path = "/" + name329elif not path and not self.is_absolute():330new_path = name331else:332parts = path.rstrip("/").split("/")333parts.append(name)334new_path = "/".join(parts)335if self.is_absolute():336new_path = self._normalize_path(new_path)337return URL(338self._val._replace(path=new_path, query="", fragment=""), encoded=True339)340341def __mod__(self, query):342return self.update_query(query)343344def __bool__(self) -> bool:345return bool(346self._val.netloc or self._val.path or self._val.query or self._val.fragment347)348349def __getstate__(self):350return (self._val,)351352def __setstate__(self, state):353if state[0] is None and isinstance(state[1], dict):354# default style pickle355self._val = state[1]["_val"]356else:357self._val, *unused = state358self._cache = {}359360def is_absolute(self):361"""A check for absolute URLs.362363Return True for absolute ones (having scheme or starting364with //), False otherwise.365366"""367return self.raw_host is not None368369def is_default_port(self):370"""A check for default port.371372Return True if port is default for specified scheme,373e.g. 'http://python.org' or 'http://python.org:80', False374otherwise.375376"""377if self.port is None:378return False379default = DEFAULT_PORTS.get(self.scheme)380if default is None:381return False382return self.port == default383384def origin(self):385"""Return an URL with scheme, host and port parts only.386387user, password, path, query and fragment are removed.388389"""390# TODO: add a keyword-only option for keeping user/pass maybe?391if not self.is_absolute():392raise ValueError("URL should be absolute")393if not self._val.scheme:394raise ValueError("URL should have scheme")395v = self._val396netloc = self._make_netloc(None, None, v.hostname, v.port)397val = v._replace(netloc=netloc, path="", query="", fragment="")398return URL(val, encoded=True)399400def relative(self):401"""Return a relative part of the URL.402403scheme, user, password, host and port are removed.404405"""406if not self.is_absolute():407raise ValueError("URL should be absolute")408val = self._val._replace(scheme="", netloc="")409return URL(val, encoded=True)410411@property412def scheme(self):413"""Scheme for absolute URLs.414415Empty string for relative URLs or URLs starting with //416417"""418return self._val.scheme419420@property421def raw_authority(self):422"""Encoded authority part of URL.423424Empty string for relative URLs.425426"""427return self._val.netloc428429@cached_property430def authority(self):431"""Decoded authority part of URL.432433Empty string for relative URLs.434435"""436return self._make_netloc(437self.user, self.password, self.host, self.port, encode_host=False438)439440@property441def raw_user(self):442"""Encoded user part of URL.443444None if user is missing.445446"""447# not .username448ret = self._val.username449if not ret:450return None451return ret452453@cached_property454def user(self):455"""Decoded user part of URL.456457None if user is missing.458459"""460return self._UNQUOTER(self.raw_user)461462@property463def raw_password(self):464"""Encoded password part of URL.465466None if password is missing.467468"""469return self._val.password470471@cached_property472def password(self):473"""Decoded password part of URL.474475None if password is missing.476477"""478return self._UNQUOTER(self.raw_password)479480@property481def raw_host(self):482"""Encoded host part of URL.483484None for relative URLs.485486"""487# Use host instead of hostname for sake of shortness488# May add .hostname prop later489return self._val.hostname490491@cached_property492def host(self):493"""Decoded host part of URL.494495None for relative URLs.496497"""498raw = self.raw_host499if raw is None:500return None501if "%" in raw:502# Hack for scoped IPv6 addresses like503# fe80::2%Проверка504# presence of '%' sign means only IPv6 address, so idna is useless.505return raw506return _idna_decode(raw)507508@property509def port(self):510"""Port part of URL, with scheme-based fallback.511512None for relative URLs or URLs without explicit port and513scheme without default port substitution.514515"""516return self._val.port or DEFAULT_PORTS.get(self._val.scheme)517518@property519def explicit_port(self):520"""Port part of URL, without scheme-based fallback.521522None for relative URLs or URLs without explicit port.523524"""525return self._val.port526527@property528def raw_path(self):529"""Encoded path of URL.530531/ for absolute URLs without path part.532533"""534ret = self._val.path535if not ret and self.is_absolute():536ret = "/"537return ret538539@cached_property540def path(self):541"""Decoded path of URL.542543/ for absolute URLs without path part.544545"""546return self._PATH_UNQUOTER(self.raw_path)547548@cached_property549def query(self):550"""A MultiDictProxy representing parsed query parameters in decoded551representation.552553Empty value if URL has no query part.554555"""556ret = MultiDict(parse_qsl(self.raw_query_string, keep_blank_values=True))557return MultiDictProxy(ret)558559@property560def raw_query_string(self):561"""Encoded query part of URL.562563Empty string if query is missing.564565"""566return self._val.query567568@cached_property569def query_string(self):570"""Decoded query part of URL.571572Empty string if query is missing.573574"""575return self._QS_UNQUOTER(self.raw_query_string)576577@cached_property578def path_qs(self):579"""Decoded path of URL with query."""580if not self.query_string:581return self.path582return "{}?{}".format(self.path, self.query_string)583584@cached_property585def raw_path_qs(self):586"""Encoded path of URL with query."""587if not self.raw_query_string:588return self.raw_path589return "{}?{}".format(self.raw_path, self.raw_query_string)590591@property592def raw_fragment(self):593"""Encoded fragment part of URL.594595Empty string if fragment is missing.596597"""598return self._val.fragment599600@cached_property601def fragment(self):602"""Decoded fragment part of URL.603604Empty string if fragment is missing.605606"""607return self._UNQUOTER(self.raw_fragment)608609@cached_property610def raw_parts(self):611"""A tuple containing encoded *path* parts.612613('/',) for absolute URLs if *path* is missing.614615"""616path = self._val.path617if self.is_absolute():618if not path:619parts = ["/"]620else:621parts = ["/"] + path[1:].split("/")622else:623if path.startswith("/"):624parts = ["/"] + path[1:].split("/")625else:626parts = path.split("/")627return tuple(parts)628629@cached_property630def parts(self):631"""A tuple containing decoded *path* parts.632633('/',) for absolute URLs if *path* is missing.634635"""636return tuple(self._UNQUOTER(part) for part in self.raw_parts)637638@cached_property639def parent(self):640"""A new URL with last part of path removed and cleaned up query and641fragment.642643"""644path = self.raw_path645if not path or path == "/":646if self.raw_fragment or self.raw_query_string:647return URL(self._val._replace(query="", fragment=""), encoded=True)648return self649parts = path.split("/")650val = self._val._replace(path="/".join(parts[:-1]), query="", fragment="")651return URL(val, encoded=True)652653@cached_property654def raw_name(self):655"""The last part of raw_parts."""656parts = self.raw_parts657if self.is_absolute():658parts = parts[1:]659if not parts:660return ""661else:662return parts[-1]663else:664return parts[-1]665666@cached_property667def name(self):668"""The last part of parts."""669return self._UNQUOTER(self.raw_name)670671@staticmethod672def _validate_authority_uri_abs_path(host, path):673"""Ensure that path in URL with authority starts with a leading slash.674675Raise ValueError if not.676"""677if len(host) > 0 and len(path) > 0 and not path.startswith("/"):678raise ValueError(679"Path in a URL with authority should start with a slash ('/') if set"680)681682@classmethod683def _normalize_path(cls, path):684# Drop '.' and '..' from path685686segments = path.split("/")687resolved_path = []688689for seg in segments:690if seg == "..":691try:692resolved_path.pop()693except IndexError:694# ignore any .. segments that would otherwise cause an695# IndexError when popped from resolved_path if696# resolving for rfc3986697pass698elif seg == ".":699continue700else:701resolved_path.append(seg)702703if segments[-1] in (".", ".."):704# do some post-processing here.705# if the last segment was a relative dir,706# then we need to append the trailing '/'707resolved_path.append("")708709return "/".join(resolved_path)710711if sys.version_info >= (3, 7):712713@classmethod714def _encode_host(cls, host, human=False):715try:716ip, sep, zone = host.partition("%")717ip = ip_address(ip)718except ValueError:719host = host.lower()720# IDNA encoding is slow,721# skip it for ASCII-only strings722# Don't move the check into _idna_encode() helper723# to reduce the cache size724if human or host.isascii():725return host726host = _idna_encode(host)727else:728host = ip.compressed729if sep:730host += "%" + zone731if ip.version == 6:732host = "[" + host + "]"733return host734735else:736# work around for missing str.isascii() in Python <= 3.6737@classmethod738def _encode_host(cls, host, human=False):739try:740ip, sep, zone = host.partition("%")741ip = ip_address(ip)742except ValueError:743host = host.lower()744if human:745return host746747for char in host:748if char > "\x7f":749break750else:751return host752host = _idna_encode(host)753else:754host = ip.compressed755if sep:756host += "%" + zone757if ip.version == 6:758host = "[" + host + "]"759return host760761@classmethod762def _make_netloc(763cls, user, password, host, port, encode=False, encode_host=True, requote=False764):765quoter = cls._REQUOTER if requote else cls._QUOTER766if encode_host:767ret = cls._encode_host(host)768else:769ret = host770if port:771ret = ret + ":" + str(port)772if password is not None:773if not user:774user = ""775else:776if encode:777user = quoter(user)778if encode:779password = quoter(password)780user = user + ":" + password781elif user and encode:782user = quoter(user)783if user:784ret = user + "@" + ret785return ret786787def with_scheme(self, scheme):788"""Return a new URL with scheme replaced."""789# N.B. doesn't cleanup query/fragment790if not isinstance(scheme, str):791raise TypeError("Invalid scheme type")792if not self.is_absolute():793raise ValueError("scheme replacement is not allowed for relative URLs")794return URL(self._val._replace(scheme=scheme.lower()), encoded=True)795796def with_user(self, user):797"""Return a new URL with user replaced.798799Autoencode user if needed.800801Clear user/password if user is None.802803"""804# N.B. doesn't cleanup query/fragment805val = self._val806if user is None:807password = None808elif isinstance(user, str):809user = self._QUOTER(user)810password = val.password811else:812raise TypeError("Invalid user type")813if not self.is_absolute():814raise ValueError("user replacement is not allowed for relative URLs")815return URL(816self._val._replace(817netloc=self._make_netloc(user, password, val.hostname, val.port)818),819encoded=True,820)821822def with_password(self, password):823"""Return a new URL with password replaced.824825Autoencode password if needed.826827Clear password if argument is None.828829"""830# N.B. doesn't cleanup query/fragment831if password is None:832pass833elif isinstance(password, str):834password = self._QUOTER(password)835else:836raise TypeError("Invalid password type")837if not self.is_absolute():838raise ValueError("password replacement is not allowed for relative URLs")839val = self._val840return URL(841self._val._replace(842netloc=self._make_netloc(val.username, password, val.hostname, val.port)843),844encoded=True,845)846847def with_host(self, host):848"""Return a new URL with host replaced.849850Autoencode host if needed.851852Changing host for relative URLs is not allowed, use .join()853instead.854855"""856# N.B. doesn't cleanup query/fragment857if not isinstance(host, str):858raise TypeError("Invalid host type")859if not self.is_absolute():860raise ValueError("host replacement is not allowed for relative URLs")861if not host:862raise ValueError("host removing is not allowed")863val = self._val864return URL(865self._val._replace(866netloc=self._make_netloc(val.username, val.password, host, val.port)867),868encoded=True,869)870871def with_port(self, port):872"""Return a new URL with port replaced.873874Clear port to default if None is passed.875876"""877# N.B. doesn't cleanup query/fragment878if port is not None and not isinstance(port, int):879raise TypeError("port should be int or None, got {}".format(type(port)))880if not self.is_absolute():881raise ValueError("port replacement is not allowed for relative URLs")882val = self._val883return URL(884self._val._replace(885netloc=self._make_netloc(val.username, val.password, val.hostname, port)886),887encoded=True,888)889890def with_path(self, path, *, encoded=False):891"""Return a new URL with path replaced."""892if not encoded:893path = self._PATH_QUOTER(path)894if self.is_absolute():895path = self._normalize_path(path)896if len(path) > 0 and path[0] != "/":897path = "/" + path898return URL(self._val._replace(path=path, query="", fragment=""), encoded=True)899900@classmethod901def _query_seq_pairs(cls, quoter, pairs):902for key, val in pairs:903if isinstance(val, (list, tuple)):904for v in val:905yield quoter(key) + "=" + quoter(cls._query_var(v))906else:907yield quoter(key) + "=" + quoter(cls._query_var(val))908909@staticmethod910def _query_var(v):911cls = type(v)912if issubclass(cls, str):913return v914if issubclass(cls, float):915if math.isinf(v):916raise ValueError("float('inf') is not supported")917if math.isnan(v):918raise ValueError("float('nan') is not supported")919return str(float(v))920if issubclass(cls, int) and cls is not bool:921return str(int(v))922raise TypeError(923"Invalid variable type: value "924"should be str, int or float, got {!r} "925"of type {}".format(v, cls)926)927928def _get_str_query(self, *args, **kwargs):929if kwargs:930if len(args) > 0:931raise ValueError(932"Either kwargs or single query parameter must be present"933)934query = kwargs935elif len(args) == 1:936query = args[0]937else:938raise ValueError("Either kwargs or single query parameter must be present")939940if query is None:941query = ""942elif isinstance(query, Mapping):943quoter = self._QUERY_PART_QUOTER944query = "&".join(self._query_seq_pairs(quoter, query.items()))945elif isinstance(query, str):946query = self._QUERY_QUOTER(query)947elif isinstance(query, (bytes, bytearray, memoryview)):948raise TypeError(949"Invalid query type: bytes, bytearray and memoryview are forbidden"950)951elif isinstance(query, Sequence):952quoter = self._QUERY_PART_QUOTER953# We don't expect sequence values if we're given a list of pairs954# already; only mappings like builtin `dict` which can't have the955# same key pointing to multiple values are allowed to use956# `_query_seq_pairs`.957query = "&".join(958quoter(k) + "=" + quoter(self._query_var(v)) for k, v in query959)960else:961raise TypeError(962"Invalid query type: only str, mapping or "963"sequence of (key, value) pairs is allowed"964)965966return query967968def with_query(self, *args, **kwargs):969"""Return a new URL with query part replaced.970971Accepts any Mapping (e.g. dict, multidict.MultiDict instances)972or str, autoencode the argument if needed.973974A sequence of (key, value) pairs is supported as well.975976It also can take an arbitrary number of keyword arguments.977978Clear query if None is passed.979980"""981# N.B. doesn't cleanup query/fragment982983new_query = self._get_str_query(*args, **kwargs)984return URL(985self._val._replace(path=self._val.path, query=new_query), encoded=True986)987988def update_query(self, *args, **kwargs):989"""Return a new URL with query part updated."""990s = self._get_str_query(*args, **kwargs)991new_query = MultiDict(parse_qsl(s, keep_blank_values=True))992query = MultiDict(self.query)993query.update(new_query)994995return URL(self._val._replace(query=self._get_str_query(query)), encoded=True)996997def with_fragment(self, fragment):998"""Return a new URL with fragment replaced.9991000Autoencode fragment if needed.10011002Clear fragment to default if None is passed.10031004"""1005# N.B. doesn't cleanup query/fragment1006if fragment is None:1007raw_fragment = ""1008elif not isinstance(fragment, str):1009raise TypeError("Invalid fragment type")1010else:1011raw_fragment = self._FRAGMENT_QUOTER(fragment)1012if self.raw_fragment == raw_fragment:1013return self1014return URL(self._val._replace(fragment=raw_fragment), encoded=True)10151016def with_name(self, name):1017"""Return a new URL with name (last part of path) replaced.10181019Query and fragment parts are cleaned up.10201021Name is encoded if needed.10221023"""1024# N.B. DOES cleanup query/fragment1025if not isinstance(name, str):1026raise TypeError("Invalid name type")1027if "/" in name:1028raise ValueError("Slash in name is not allowed")1029name = self._PATH_QUOTER(name)1030if name in (".", ".."):1031raise ValueError(". and .. values are forbidden")1032parts = list(self.raw_parts)1033if self.is_absolute():1034if len(parts) == 1:1035parts.append(name)1036else:1037parts[-1] = name1038parts[0] = "" # replace leading '/'1039else:1040parts[-1] = name1041if parts[0] == "/":1042parts[0] = "" # replace leading '/'1043return URL(1044self._val._replace(path="/".join(parts), query="", fragment=""),1045encoded=True,1046)10471048def join(self, url):1049"""Join URLs10501051Construct a full (“absolute”) URL by combining a “base URL”1052(self) with another URL (url).10531054Informally, this uses components of the base URL, in1055particular the addressing scheme, the network location and1056(part of) the path, to provide missing components in the1057relative URL.10581059"""1060# See docs for urllib.parse.urljoin1061if not isinstance(url, URL):1062raise TypeError("url should be URL")1063return URL(urljoin(str(self), str(url)), encoded=True)10641065def human_repr(self):1066"""Return decoded human readable string for URL representation."""1067user = _human_quote(self.user, "#/:?@")1068password = _human_quote(self.password, "#/:?@")1069host = self.host1070if host:1071host = self._encode_host(self.host, human=True)1072path = _human_quote(self.path, "#?")1073query_string = "&".join(1074"{}={}".format(_human_quote(k, "#&+;="), _human_quote(v, "#&+;="))1075for k, v in self.query.items()1076)1077fragment = _human_quote(self.fragment, "")1078return urlunsplit(1079SplitResult(1080self.scheme,1081self._make_netloc(1082user,1083password,1084host,1085self._val.port,1086encode_host=False,1087),1088path,1089query_string,1090fragment,1091)1092)109310941095def _human_quote(s, unsafe):1096if not s:1097return s1098for c in "%" + unsafe:1099if c in s:1100s = s.replace(c, "%{:02X}".format(ord(c)))1101if s.isprintable():1102return s1103return "".join(c if c.isprintable() else quote(c) for c in s)110411051106_MAXCACHE = 256110711081109@functools.lru_cache(_MAXCACHE)1110def _idna_decode(raw):1111try:1112return idna.decode(raw.encode("ascii"))1113except UnicodeError: # e.g. '::1'1114return raw.encode("ascii").decode("idna")111511161117@functools.lru_cache(_MAXCACHE)1118def _idna_encode(host):1119try:1120return idna.encode(host, uts46=True).decode("ascii")1121except UnicodeError:1122return host.encode("idna").decode("ascii")112311241125@rewrite_module1126def cache_clear():1127_idna_decode.cache_clear()1128_idna_encode.cache_clear()112911301131@rewrite_module1132def cache_info():1133return {1134"idna_encode": _idna_encode.cache_info(),1135"idna_decode": _idna_decode.cache_info(),1136}113711381139@rewrite_module1140def cache_configure(*, idna_encode_size=_MAXCACHE, idna_decode_size=_MAXCACHE):1141global _idna_decode, _idna_encode11421143_idna_encode = functools.lru_cache(idna_encode_size)(_idna_encode.__wrapped__)1144_idna_decode = functools.lru_cache(idna_decode_size)(_idna_decode.__wrapped__)114511461147