Package: xri
XRIXRI is a small Python library for efficient and RFC-correct representation of URIs and IRIs.
It is currently work-in-progress and, as such, is not recommended for production environments.The generic syntax for URIs is defined inRFC 3986.
This is extended in the IRI specification,RFC 3987, to support extended characters outside of the ASCII range.
TheURIandIRItypes defined in this library implement those definitions and store their constituent parts asbytesorstrvalues respectively.Creating a URI or IRITo get started, simply pass a string value into theURIorIRIconstructor.
These can both accept eitherbytesorstrvalues, and will encode or decode UTF-8 values as required.>>> from xri import URI
>>> uri = URI("http://alice@example.com/a/b/c?q=x#z")
>>> uri
<URI scheme=b'http' authority=URI.Authority(b'example.com', userinfo=b'alice') \
     path=URI.Path(b'/a/b/c') query=b'q=x' fragment=b'z'>
>>> uri.scheme = "https"
>>> print(uri)
https://alice@example.com/a/b/c?q=x#zComponent partsEachURIorIRIobject is fully mutable, allowing any component parts to be get, set, or deleted.
The following component parts are available:URI/IRIobject.scheme(None or string).authority(None orAuthorityobject).userinfo(None or string).host(string).port(None, string or int).path(Pathobject - can be used as an iterable of segment strings).query(None orQueryobject).fragment(None or string)(The type "string" here refers tobytesorbytearrayforURIobjects, andstrforIRIobjects.)Percent encoding and decodingEach of theURIandIRIclasses has class methods calledpct_encodeandpct_decode.
These operate slightly differently, depending on the base class, as a slightly different set of characters are kept "safe" during encoding.>>>URI.pct_encode("abc/def")'abc%2Fdef'>>>URI.pct_encode("abc/def",safe="/")'abc/def'>>>URI.pct_encode("20% of $125 is $25")'20%25%20of%20%24125%20is%20%2425'>>>URI.pct_encode("20% of £125 is £25")# '£' is encoded with UTF-8'20%25%20of%20%C2%A3125%20is%20%C2%A325'>>>IRI.pct_encode("20% of £125 is £25")# '£' is safe within an IRI'20%25%20of%20£125%20is%20£25'>>>URI.pct_decode('20%25%20of%20%C2%A3125%20is%20%C2%A325')# str in, str out (using UTF-8)'20% of £125 is £25'>>>URI.pct_decode(b'20%25%20of%20%C2%A3125%20is%20%C2%A325')# bytes in, bytes out (no UTF-8)b'20% of\xc2\xa3125 is\xc2\xa325'Safe characters (passed in via thesafeargument) can only be drawn from the set below.
Other characters passed to this argument will give aValueError.! # $ & ' ( ) * + , / : ; = ? @ [ ]Advantages over built-inurllib.parsemoduleCorrect handling of character encodingsRFC 3986 specifies that extended characters (beyond the ASCII range) are not supported directly within URIs.
When used, these should always be encoded with UTF-8 before percent encoding.
IRIs (defined in RFC 3987) do however allow such characters.urllib.parsedoes not enforce this behaviour according to the RFCs, and does not support UTF-8 encoded bytes as input values.>>>urlparse("https://example.com/ä").path'/ä'>>>urlparse("https://example.com/ä".encode("utf-8")).pathUnicodeDecodeError:'ascii'codeccan't decode byte 0xc3 in position 20: ordinal not in range(128)Conversely,xrihandles these scenarios correctly according to the RFCs.>>>URI("https://example.com/ä").pathURI.Path(b'/%C3%A4')>>>URI("https://example.com/ä".encode("utf-8")).pathURI.Path(b'/%C3%A4')>>>IRI("https://example.com/ä").pathIRI.Path('/ä')>>>IRI("https://example.com/ä".encode("utf-8")).pathIRI.Path('/ä')Optional components may be emptyOptional URI components, such asqueryandfragmentare allowed to be present but empty,according to RFC 3986.
As such, there is a semantic difference between an empty component and a missing component.
When composed, this will be denoted by the absence or presence of a marker character ('?'in the case of the query component).Theurlparsefunction does not distinguish between empty and missing components;
both are treated as "missing".>>>urlparse("https://example.com/a").geturl()'https://example.com/a'>>>urlparse("https://example.com/a?").geturl()'https://example.com/a'xri, on the other hand, correctly distinguishes between these cases:>>>str(URI("https://example.com/a"))'https://example.com/a'>>>str(URI("https://example.com/a?"))'https://example.com/a?'