Skip to content

Instantly share code, notes, and snippets.

@technige
Created February 4, 2021 08:59
Show Gist options
  • Save technige/3bb8e37ec2d238d5687f172da500a9c8 to your computer and use it in GitHub Desktop.
Save technige/3bb8e37ec2d238d5687f172da500a9c8 to your computer and use it in GitHub Desktop.
Implementation of RFC 3986 -- Uniform Resource Identifier (URI): Generic Syntax
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2012-2015, Nigel Small
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
An implementation of URIs from RFC 3986 (URI Generic Syntax).
See: http://www.ietf.org/rfc/rfc3986.txt
"""
from __future__ import unicode_literals
from .kvlist import KeyValueList
from .util import bstr, ustr
__all__ = ["general_delimiters", "subcomponent_delimiters",
"reserved", "unreserved", "percent_encode", "percent_decode",
"ParameterString", "Authority", "Path", "Query", "URI"]
percent_codes = dict(zip(bytes(bytearray(range(256))),
[bstr("%" + hex(n)[2:].upper().zfill(2)) for n in range(256)]))
unhex = dict(zip(b"0123456789ABCDEFabcdef", list(range(0, 16)) + list(range(10, 16))))
# RFC 3986 § 2.2.
general_delimiters = ":/?#[]@"
subcomponent_delimiters = "!$&'()*+,;="
reserved = general_delimiters + subcomponent_delimiters
# RFC 3986 § 2.3.
unreserved = bstr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-._~")
# RFC 3986 § 2.1.
def percent_encode(data, safe=None):
""" Percent encode a string of data, optionally keeping certain characters
unencoded.
"""
if data is None:
return None
if isinstance(data, (tuple, list, set)):
return "&".join(
percent_encode(value, safe=safe)
for value in data
)
if isinstance(data, dict):
return "&".join(
key + "=" + percent_encode(value, safe=safe)
for key, value in data.items()
)
unreserved_and_safe = unreserved + bstr(safe or b"")
chars = bstr(data)
out = []
p = 0
for i, char in enumerate(chars):
if char == b"%" or char not in unreserved_and_safe:
out.append(chars[p:i])
out.append(percent_codes[char])
p = i + 1
out.append(chars[p:])
if isinstance(data, (bytes, bytearray)):
return b"".join(out)
else:
return u"".join(map(ustr, out))
def percent_decode(data):
""" Percent decode a string of data.
"""
if data is None:
return None
parts = bstr(data).split(b"%")
out = [parts[0].replace(b"+", b" ")]
for i, part in enumerate(parts[1:], start=1):
out.append(bytes(bytearray([(unhex[part[0]] << 4) + unhex[part[1]]])))
out.append(part[2:].replace(b"+", b" "))
return b"".join(out).decode("utf-8")
class Part(object):
""" Internal base class for all URI component parts.
"""
@classmethod
def _cast(cls, obj):
""" Convert the object supplied to an instance of this class, if
possible.
"""
if obj is None:
return cls(None)
elif isinstance(obj, cls):
return obj
else:
return cls(ustr(obj))
def __init__(self):
pass
def __repr__(self):
if self.string is None:
return "%s" % self.__class__.__name__
else:
return "%s(%r)" % (self.__class__.__name__, self.string)
def __str__(self):
return self.string or ""
def __eq__(self, other):
if other is None:
return self.string is None
try:
other_string = other.string
except AttributeError:
other_string = self._cast(other).string
return self.string == other_string
def __ne__(self, other):
return not self.__eq__(other)
def __bool__(self):
return bool(self.string)
def __nonzero__(self):
return bool(self.string)
def __len__(self):
return len(ustr(self))
def __iter__(self):
return iter(self.string)
@property
def string(self):
raise NotImplementedError()
class ParameterString(Part):
__string = NotImplemented
def __init__(self, string, separator):
super(ParameterString, self).__init__()
self.__separator = separator
self.__none = string is None
self.__parameters = KeyValueList()
if string:
bits = string.split(self.__separator)
for bit in bits:
if "=" in bit:
key, value = map(percent_decode, bit.partition("=")[0::2])
else:
key, value = percent_decode(bit), None
self.__parameters.append(key, value)
def __len__(self):
return self.__parameters.__len__()
def __bool__(self):
return bool(self.__parameters)
def __nonzero__(self):
return bool(self.__parameters)
def __contains__(self, item):
return self.__parameters.__contains__(item)
def __hash__(self):
return hash(self.string)
def __iter__(self):
return self.__parameters.__iter__()
def __getitem__(self, index):
if isinstance(index, slice):
out = ParameterString("", self.__separator)
out.__parameters.extend(self.__parameters.__getitem__(index))
return out
else:
return self.__parameters.__getitem__(index)
def __getslice__(self, start, stop):
out = ParameterString("", self.__separator)
out.__parameters.extend(self.__parameters.__getslice__(start, stop))
return out
def get(self, name, index=0):
if not self.__parameters.has_key(name):
raise KeyError(name)
for i, value in enumerate(self.__parameters.get(name)):
if i == index:
return value
raise IndexError("Parameter {0} does not have {1} "
"values".format(name, index))
def get_all(self, name):
if not self.__parameters.has_key(name):
raise KeyError(name)
return list(self.__parameters.get(name))
@property
def string(self):
if self.__string is NotImplemented:
if self.__none:
self.__string = None
else:
bits = []
for key, value in self.__parameters:
if value is None:
bits.append(percent_encode(key))
else:
bits.append(percent_encode(key) + "=" + percent_encode(value))
self.__string = self.__separator.join(bits)
return self.__string
class Authority(Part):
""" A host name plus optional port and user information detail.
**Syntax**
``authority := [ user_info "@" ] host [ ":" port ]``
.. seealso::
`RFC 3986 § 3.2`_
.. _`RFC 3986 § 3.2`: http://tools.ietf.org/html/rfc3986#section-3.2
"""
@classmethod
def _parse_host_port(cls, string):
host, colon, port = string.partition(":")
if colon:
return host, int(port)
else:
return host, None
__instances = {}
def __new__(cls, string=None):
if string is None:
return super(Authority, cls).__new__(cls)
try:
inst = cls.__instances[string]
except KeyError:
inst = super(Authority, cls).__new__(cls)
user_info, at, host_port = string.rpartition("@")
if at:
inst.__user_info = percent_decode(user_info)
inst.__host, inst.__port = cls._parse_host_port(host_port)
cls.__instances[string] = inst
return inst
__user_info = None
__host = None
__port = None
__string = NotImplemented
def __init__(self, string=None):
super(Authority, self).__init__()
def __bool__(self):
return bool(self.__user_info or self.__host or self.__port)
def __nonzero__(self):
return bool(self.__user_info or self.__host or self.__port)
def __hash__(self):
return hash(self.string)
@property
def host(self):
""" The host part of this authority component, an empty string if host
is empty or :py:const:`None` if undefined.
::
>>> Authority(None).host
None
>>> Authority("").host
''
>>> Authority("example.com").host
'example.com'
>>> Authority("example.com:8080").host
'example.com'
>>> Authority("bob@example.com").host
'example.com'
>>> Authority("bob@example.com:8080").host
'example.com'
:return:
"""
return self.__host
@property
def host_port(self):
""" The host and port parts of this authority component or
:py:const:`None` if undefined.
::
>>> Authority(None).host_port
None
>>> Authority("").host_port
''
>>> Authority("example.com").host_port
'example.com'
>>> Authority("example.com:8080").host_port
'example.com:8080'
>>> Authority("bob@example.com").host_port
'example.com'
>>> Authority("bob@example.com:8080").host_port
'example.com:8080'
:return:
"""
u = [self.__host]
if self.__port is not None:
u += [":", ustr(self.__port)]
return "".join(u)
@property
def port(self):
""" The port part of this authority component or :py:const:`None` if
undefined.
::
>>> Authority(None).port
None
>>> Authority("").port
None
>>> Authority("example.com").port
None
>>> Authority("example.com:8080").port
8080
>>> Authority("bob@example.com").port
None
>>> Authority("bob@example.com:8080").port
8080
:return:
"""
return self.__port
@property
def string(self):
""" The full string value of this authority component or
:`py:const:`None` if undefined.
::
>>> Authority(None).string
None
>>> Authority("").string
''
>>> Authority("example.com").string
'example.com'
>>> Authority("example.com:8080").string
'example.com:8080'
>>> Authority("bob@example.com").string
'bob@example.com'
>>> Authority("bob@example.com:8080").string
'bob@example.com:8080'
:return:
"""
if self.__string is NotImplemented:
if self.__host is None:
self.__string = None
else:
u = []
if self.__user_info is not None:
u += [percent_encode(self.__user_info), "@"]
u += [self.__host]
if self.__port is not None:
u += [":", ustr(self.__port)]
self.__string = "".join(u)
return self.__string
@property
def user_info(self):
""" The user information part of this authority component or
:py:const:`None` if undefined.
::
>>> Authority(None).user_info
None
>>> Authority("").user_info
None
>>> Authority("example.com").user_info
None
>>> Authority("example.com:8080").user_info
None
>>> Authority("bob@example.com").user_info
'bob'
>>> Authority("bob@example.com:8080").user_info
'bob'
:return:
"""
return self.__user_info
def with_user_info(self, string):
target = Authority(None)
target.__user_info = percent_decode(string)
target.__host = self.__host
target.__port = self.__port
return target
def with_host_port(self, string):
target = Authority(None)
target.__user_info = self.__user_info
target.__host, target.__port = self._parse_host_port(string)
return target
def with_host(self, string):
target = Authority(None)
target.__user_info = self.__user_info
target.__host = string
target.__port = self.__port
return target
def with_port(self, value):
target = Authority(None)
target.__user_info = self.__user_info
target.__host = self.__host
target.__port = int(value)
return target
class Path(Part):
__segments = None
__string = NotImplemented
def __init__(self, string):
super(Path, self).__init__()
if string is not None:
self.__segments = tuple(map(percent_decode, string.split("/")))
def __hash__(self):
return hash(self.string)
@property
def string(self):
if self.__string is NotImplemented:
if self.__segments is None:
self.__string = None
else:
self.__string = "/".join(map(percent_encode, self.__segments))
return self.__string
@property
def segments(self):
if self.__segments is None:
return ()
else:
return self.__segments
def __iter__(self):
return iter(self.__segments)
def remove_dot_segments(self):
""" Implementation of RFC3986, section 5.2.4
"""
inp = self.string
out = ""
while inp:
if inp.startswith("../"):
inp = inp[3:]
elif inp.startswith("./"):
inp = inp[2:]
elif inp.startswith("/./"):
inp = inp[2:]
elif inp == "/.":
inp = "/"
elif inp.startswith("/../"):
inp = inp[3:]
out = out.rpartition("/")[0]
elif inp == "/..":
inp = "/"
out = out.rpartition("/")[0]
elif inp in (".", ".."):
inp = ""
else:
if inp.startswith("/"):
inp = inp[1:]
out += "/"
seg, slash, inp = inp.partition("/")
out += seg
inp = slash + inp
return Path(out)
def with_trailing_slash(self):
if self.__segments is None:
return self
s = self.string
if s.endswith("/"):
return self
else:
return Path(s + "/")
def without_trailing_slash(self):
if self.__segments is None:
return self
s = self.string
if s.endswith("/"):
return Path(s[:-1])
else:
return self
class Query(ParameterString):
SEPARATOR = "&"
def __init__(self, string):
super(Query, self).__init__(string, self.SEPARATOR)
def __hash__(self):
return hash(self.string)
class URI(Part):
""" Uniform Resource Identifier.
.. seealso::
`RFC 3986`_
.. _`RFC 3986`: http://tools.ietf.org/html/rfc3986
"""
@classmethod
def build(cls, **parts):
""" Build a URI object from named parts. The part names available are:
- string
- hierarchical_part
- absolute_path_reference
- authority
- host_port
- scheme
- user_info
- host
- port
- path
- query
- fragment
"""
uri = URI(parts.get("string"))
uri.__set_hierarchical_part(parts.get("hierarchical_part"))
uri.__set_absolute_path_reference(parts.get("absolute_path_reference"))
uri.__set_authority(parts.get("authority"))
uri.__set_host_port(parts.get("host_port"))
uri.__set_scheme(parts.get("scheme"))
uri.__set_user_info(parts.get("user_info"))
uri.__set_host(parts.get("host"))
uri.__set_port(parts.get("port"))
uri.__set_path(parts.get("path"))
uri.__set_query(parts.get("query"))
uri.__set_fragment(parts.get("fragment"))
if parts and uri.__path is None:
uri.__path = Path("")
return uri
@classmethod
def _partition_fragment(cls, value):
value, _, fragment = value.partition("#")
if fragment:
return value, percent_decode(fragment)
else:
return value, None
@classmethod
def _partition_query(cls, value):
value, question_mark, query = value.partition("?")
if query:
return value, Query(query)
else:
return value, None
@classmethod
def _parse_hierarchical_part(cls, value):
if value.startswith("//"):
authority, slash, path = value[2:].partition("/")
if slash:
return Authority(authority), Path(slash + path)
else:
return Authority(authority), Path("")
else:
return None, Path(value)
def __new__(cls, value=None):
if isinstance(value, cls):
return value
inst = super(cls, URI).__new__(cls)
if value is None:
return inst
try:
if value.__uri__ is None:
return inst
except AttributeError:
pass
try:
value = ustr(value.__uri__)
except AttributeError:
value = ustr(value)
# scheme
if ":" in value:
inst.__scheme, _, value = value.partition(":")
inst.__scheme = percent_decode(inst.__scheme)
else:
inst.__scheme = None
# fragment
value, inst.__fragment = cls._partition_fragment(value)
# query
value, inst.__query = cls._partition_query(value)
# hierarchical part
inst.__authority, inst.__path = cls._parse_hierarchical_part(value)
return inst
__scheme = None
__authority = None
__path = None
__query = None
__fragment = None
__string = NotImplemented
def __init__(self, value=None):
Part.__init__(self)
def __hash__(self):
return hash(self.string)
def __add__(self, other):
return URI("{0}{1}".format(self, other))
@property
def __uri__(self):
return self.string
def __set_hierarchical_part(self, string):
if string is not None:
self.__authority, self.__path = self._parse_hierarchical_part(string)
def __set_absolute_path_reference(self, string):
if string is not None:
string, self.__fragment = self._partition_fragment(string)
string, self.__query = self._partition_query(string)
self.__path = Path(string)
def __set_authority(self, string):
if string is not None:
self.__authority = Authority(string)
def __set_host_port(self, string):
if string is not None:
if self.__authority is None:
self.__authority = Authority(string)
else:
host, port = Authority._parse_host_port(string)
self.__authority._Authority__host = host
self.__authority._Authority__port = port
def __set_scheme(self, string):
if string is not None:
self.__scheme = string
def __set_user_info(self, string):
if string is not None:
if self.__authority is None:
self.__authority = Authority("")
self.__authority._Authority__user_info = string
def __set_host(self, string):
if string is not None:
if self.__authority is None:
self.__authority = Authority(string)
else:
self.__authority._Authority__host = string
def __set_port(self, number):
if number is not None:
if self.__authority is None:
self.__authority = Authority("")
self.__authority._Authority__port = number
def __set_path(self, string):
if string is not None:
self.__path = Path(string)
def __set_query(self, string):
if string is not None:
self.__query = Query(string)
def __set_fragment(self, string):
if string is not None:
self.__fragment = string
@property
def string(self):
""" The full percent-encoded string value of this URI or
:py:const:`None` if undefined.
::
>>> URI(None).string
None
>>> URI("").string
''
>>> URI("http://example.com").string
'example.com'
>>> URI("foo/bar").string
'foo/bar'
>>> URI("http://bob@example.com:8080/data/report.html?date=2000-12-25#summary").string
'http://bob@example.com:8080/data/report.html?date=2000-12-25#summary'
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\___________________________________________________________________/
|
string
:rtype: percent-encoded string or :py:const:`None`
.. note::
Unlike ``string``, the ``__str__`` method will always return a
string, even when the URI is undefined; in this case, an empty
string is returned instead of :py:const:`None`.
"""
if self.__string is NotImplemented:
if self.__path is None:
self.__string = None
else:
u = []
if self.__scheme is not None:
u += [percent_encode(self.__scheme), ":"]
if self.__authority is not None:
u += ["//", ustr(self.__authority)]
u += [ustr(self.__path)]
if self.__query is not None:
u += ["?", ustr(self.__query)]
if self.__fragment is not None:
u += ["#", percent_encode(self.__fragment)]
self.__string = "".join(u)
return self.__string
@property
def scheme(self):
""" The scheme part of this URI or :py:const:`None` if undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\___/
|
scheme
:rtype: unencoded string or :py:const:`None`
"""
return self.__scheme
@property
def authority(self):
""" The authority part of this URI or :py:const:`None` if undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\__________________/
|
authority
:rtype: :py:class:`Authority <httpstream.uri.Authority>` instance or
:py:const:`None`
"""
return self.__authority
@property
def user_info(self):
""" The user information part of this URI or :py:const:`None` if
undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\_/
|
user_info
:return: string value of user information part or :py:const:`None`
:rtype: unencoded string or :py:const:`None`
"""
if self.__authority is None:
return None
else:
return self.__authority.user_info
@property
def host(self):
""" The *host* part of this URI or :py:const:`None` if undefined.
::
>>> URI(None).host
None
>>> URI("").host
None
>>> URI("http://example.com").host
'example.com'
>>> URI("http://example.com:8080/data").host
'example.com'
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\_________/
|
host
:return:
:rtype: unencoded string or :py:const:`None`
"""
if self.__authority is None:
return None
else:
return self.__authority.host
@property
def port(self):
""" The *port* part of this URI or :py:const:`None` if undefined.
::
>>> URI(None).port
None
>>> URI("").port
None
>>> URI("http://example.com").port
None
>>> URI("http://example.com:8080/data").port
8080
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\__/
|
port
:return:
:rtype: integer or :py:const:`None`
"""
if self.__authority is None:
return None
else:
return self.__authority.port
@property
def host_port(self):
""" The *host* and *port* parts of this URI separated by a colon or
:py:const:`None` if both are undefined.
::
>>> URI(None).host_port
None
>>> URI("").host_port
None
>>> URI("http://example.com").host_port
'example.com'
>>> URI("http://example.com:8080/data").host_port
'example.com:8080'
>>> URI("http://bob@example.com:8080/data").host_port
'example.com:8080'
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\______________/
|
host_port
:return:
:rtype: percent-encoded string or :py:const:`None`
"""
if self.__authority is None:
return None
else:
return self.__authority.host_port
@property
def path(self):
""" The *path* part of this URI or :py:const:`None` if undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\_______________/
|
path
:return:
:rtype: :py:class:`Path <httpstream.uri.Path>` instance or
:py:const:`None`
"""
return self.__path
@property
def query(self):
""" The *query* part of this URI or :py:const:`None` if undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\_____________/
|
query
:rtype: :py:class:`Query <httpstream.uri.Query>` instance or
:py:const:`None`
"""
return self.__query
@property
def fragment(self):
""" The *fragment* part of this URI or :py:const:`None` if undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\_____/
|
fragment
:return:
:rtype: unencoded string or :py:const:`None`
"""
return self.__fragment
@property
def hierarchical_part(self):
""" The authority and path parts of this URI or :py:const:`None` if
undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\___________________________________/
|
hierarchical_part
:return: combined string values of authority and path parts or
:py:const:`None`
:rtype: percent-encoded string or :py:const:`None`
"""
if self.__path is None:
return None
u = []
if self.__authority is not None:
u += ["//", ustr(self.__authority)]
u += [ustr(self.__path)]
return "".join(u)
@property
def absolute_path_reference(self):
""" The path, query and fragment parts of this URI or :py:const:`None`
if undefined.
**Component Definition:**
::
https://bob@example.com:8080/data/report.html?date=2000-12-25#summary
\_______________________________________/
|
absolute_path_reference
:return: combined string values of path, query and fragment parts or
:py:const:`None`
:rtype: percent-encoded string or :py:const:`None`
"""
if self.__path is None:
return None
u = [ustr(self.__path)]
if self.__query is not None:
u += ["?", ustr(self.__query)]
if self.__fragment is not None:
u += ["#", percent_encode(self.__fragment)]
return "".join(u)
def _merge_path(self, relative_path_reference):
if self.__authority is not None and not self.__path:
return Path("/" + ustr(relative_path_reference))
elif "/" in self.__path.string:
segments = self.__path.segments[:-1] + ("",)
return Path("/".join(segments) + ustr(relative_path_reference))
else:
return relative_path_reference
def resolve(self, reference, strict=True):
""" Transform a reference relative to this URI to produce a full target
URI.
.. seealso::
`RFC 3986 § 5.2.2`_
.. _`RFC 3986 § 5.2.2`: http://tools.ietf.org/html/rfc3986#section-5.2.2
"""
if reference is None:
return None
reference = self._cast(reference)
target = URI(None)
if not strict and reference.__scheme == self.__scheme:
reference_scheme = None
else:
reference_scheme = reference.__scheme
if reference_scheme is not None:
target.__scheme = reference_scheme
target.__authority = reference.__authority
target.__path = reference.__path.remove_dot_segments()
target.__query = reference.__query
else:
if reference.__authority is not None:
target.__authority = reference.__authority
target.__path = reference.__path.remove_dot_segments()
target.__query = reference.__query
else:
if not reference.path:
target.__path = self.__path
if reference.__query is not None:
target.__query = reference.__query
else:
target.__query = self.__query
else:
if ustr(reference.__path).startswith("/"):
target.__path = reference.__path.remove_dot_segments()
else:
target.__path = self._merge_path(reference.__path)
target.__path = target.__path.remove_dot_segments()
target.__query = reference.__query
target.__authority = self.__authority
target.__scheme = self.__scheme
target.__fragment = reference.__fragment
return target
def with_hierarchical_part(self, string):
target = URI(None)
target.__scheme = self.__scheme
target.__authority, target.__path = self._parse_hierarchical_part(string)
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_absolute_path_reference(self, string):
target = URI(None)
target.__scheme = self.__scheme
target.__authority = self.__authority
string, target.__fragment = self._partition_fragment(string)
string, target.__query = self._partition_query(string)
target.__path = Path(string)
return target
def with_authority(self, string):
target = URI(None)
target.__scheme = self.__scheme
target.__authority = Authority(string)
target.__path = self.__path
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_scheme(self, string):
target = URI(None)
target.__scheme = percent_decode(string)
target.__authority = self.__authority
target.__path = self.__path or Path("")
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_user_info(self, string):
target = URI(None)
target.__scheme = self.__scheme
if self.__authority:
target.__authority = self.__authority.with_user_info(string)
else:
target.__authority = Authority().with_user_info(string)
target.__path = self.__path or Path("")
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_host_port(self, string):
target = URI(None)
target.__scheme = self.__scheme
if self.__authority:
target.__authority = self.__authority.with_host_port(string)
else:
target.__authority = Authority().with_host_port(string)
target.__path = self.__path or Path("")
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_host(self, string):
target = URI(None)
target.__scheme = self.__scheme
if self.__authority:
target.__authority = self.__authority.with_host(string)
else:
target.__authority = Authority().with_host(string)
target.__path = self.__path or Path("")
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_port(self, value):
target = URI(None)
target.__scheme = self.__scheme
if self.__authority:
target.__authority = self.__authority.with_port(value)
else:
target.__authority = Authority().with_port(value)
target.__path = self.__path or Path("")
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_path(self, string):
target = URI(None)
target.__scheme = self.__scheme
target.__authority = self.__authority
target.__path = Path(string)
target.__query = self.__query
target.__fragment = self.__fragment
return target
def with_query(self, string):
target = URI(None)
target.__scheme = self.__scheme
target.__authority = self.__authority
target.__path = self.__path or Path("")
target.__query = Query(string)
target.__fragment = self.__fragment
return target
def with_fragment(self, string):
target = URI(None)
target.__scheme = self.__scheme
target.__authority = self.__authority
target.__path = self.__path or Path("")
target.__query = self.__query
target.__fragment = percent_decode(string)
return target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment