Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/rfc3986/iri.py: 44%
50 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:04 +0000
1"""Module containing the implementation of the IRIReference class."""
2# Copyright (c) 2014 Rackspace
3# Copyright (c) 2015 Ian Stapleton Cordasco
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16from collections import namedtuple
18from . import compat
19from . import exceptions
20from . import misc
21from . import normalizers
22from . import uri
25try:
26 import idna
27except ImportError: # pragma: no cover
28 idna = None
31class IRIReference(
32 namedtuple("IRIReference", misc.URI_COMPONENTS), uri.URIMixin
33):
34 """Immutable object representing a parsed IRI Reference.
36 Can be encoded into an URIReference object via the procedure
37 specified in RFC 3987 Section 3.1
39 .. note::
40 The IRI submodule is a new interface and may possibly change in
41 the future. Check for changes to the interface when upgrading.
42 """
44 slots = ()
46 def __new__(
47 cls, scheme, authority, path, query, fragment, encoding="utf-8"
48 ):
49 """Create a new IRIReference."""
50 ref = super().__new__(
51 cls,
52 scheme or None,
53 authority or None,
54 path or None,
55 query,
56 fragment,
57 )
58 ref.encoding = encoding
59 return ref
61 def __eq__(self, other):
62 """Compare this reference to another."""
63 other_ref = other
64 if isinstance(other, tuple):
65 other_ref = self.__class__(*other)
66 elif not isinstance(other, IRIReference):
67 try:
68 other_ref = self.__class__.from_string(other)
69 except TypeError:
70 raise TypeError(
71 "Unable to compare {}() to {}()".format(
72 type(self).__name__, type(other).__name__
73 )
74 )
76 # See http://tools.ietf.org/html/rfc3986#section-6.2
77 return tuple(self) == tuple(other_ref)
79 def _match_subauthority(self):
80 return misc.ISUBAUTHORITY_MATCHER.match(self.authority)
82 @classmethod
83 def from_string(cls, iri_string, encoding="utf-8"):
84 """Parse a IRI reference from the given unicode IRI string.
86 :param str iri_string: Unicode IRI to be parsed into a reference.
87 :param str encoding: The encoding of the string provided
88 :returns: :class:`IRIReference` or subclass thereof
89 """
90 iri_string = compat.to_str(iri_string, encoding)
92 split_iri = misc.IRI_MATCHER.match(iri_string).groupdict()
93 return cls(
94 split_iri["scheme"],
95 split_iri["authority"],
96 normalizers.encode_component(split_iri["path"], encoding),
97 normalizers.encode_component(split_iri["query"], encoding),
98 normalizers.encode_component(split_iri["fragment"], encoding),
99 encoding,
100 )
102 def encode(self, idna_encoder=None): # noqa: C901
103 """Encode an IRIReference into a URIReference instance.
105 If the ``idna`` module is installed or the ``rfc3986[idna]``
106 extra is used then unicode characters in the IRI host
107 component will be encoded with IDNA2008.
109 :param idna_encoder:
110 Function that encodes each part of the host component
111 If not given will raise an exception if the IRI
112 contains a host component.
113 :rtype: uri.URIReference
114 :returns: A URI reference
115 """
116 authority = self.authority
117 if authority:
118 if idna_encoder is None:
119 if idna is None: # pragma: no cover
120 raise exceptions.MissingDependencyError(
121 "Could not import the 'idna' module "
122 "and the IRI hostname requires encoding"
123 )
125 def idna_encoder(name):
126 if any(ord(c) > 128 for c in name):
127 try:
128 return idna.encode(
129 name.lower(), strict=True, std3_rules=True
130 )
131 except idna.IDNAError:
132 raise exceptions.InvalidAuthority(self.authority)
133 return name
135 authority = ""
136 if self.host:
137 authority = ".".join(
138 [
139 compat.to_str(idna_encoder(part))
140 for part in self.host.split(".")
141 ]
142 )
144 if self.userinfo is not None:
145 authority = (
146 normalizers.encode_component(self.userinfo, self.encoding)
147 + "@"
148 + authority
149 )
151 if self.port is not None:
152 authority += ":" + str(self.port)
154 return uri.URIReference(
155 self.scheme,
156 authority,
157 path=self.path,
158 query=self.query,
159 fragment=self.fragment,
160 encoding=self.encoding,
161 )