Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/rfc3986/_mixin.py: 69%
121 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 URIMixin class."""
2import warnings
4from . import exceptions as exc
5from . import misc
6from . import normalizers
7from . import validators
10class URIMixin:
11 """Mixin with all shared methods for URIs and IRIs."""
13 __hash__ = tuple.__hash__
15 def authority_info(self):
16 """Return a dictionary with the ``userinfo``, ``host``, and ``port``.
18 If the authority is not valid, it will raise a
19 :class:`~rfc3986.exceptions.InvalidAuthority` Exception.
21 :returns:
22 ``{'userinfo': 'username:password', 'host': 'www.example.com',
23 'port': '80'}``
24 :rtype: dict
25 :raises rfc3986.exceptions.InvalidAuthority:
26 If the authority is not ``None`` and can not be parsed.
27 """
28 if not self.authority:
29 return {"userinfo": None, "host": None, "port": None}
31 match = self._match_subauthority()
33 if match is None:
34 # In this case, we have an authority that was parsed from the URI
35 # Reference, but it cannot be further parsed by our
36 # misc.SUBAUTHORITY_MATCHER. In this case it must not be a valid
37 # authority.
38 raise exc.InvalidAuthority(self.authority.encode(self.encoding))
40 # We had a match, now let's ensure that it is actually a valid host
41 # address if it is IPv4
42 matches = match.groupdict()
43 host = matches.get("host")
45 if (
46 host
47 and misc.IPv4_MATCHER.match(host)
48 and not validators.valid_ipv4_host_address(host)
49 ):
50 # If we have a host, it appears to be IPv4 and it does not have
51 # valid bytes, it is an InvalidAuthority.
52 raise exc.InvalidAuthority(self.authority.encode(self.encoding))
54 return matches
56 def _match_subauthority(self):
57 return misc.SUBAUTHORITY_MATCHER.match(self.authority)
59 @property
60 def _validator(self):
61 v = getattr(self, "_cached_validator", None)
62 if v is not None:
63 return v
64 self._cached_validator = validators.Validator().require_presence_of(
65 "scheme"
66 )
67 return self._cached_validator
69 @property
70 def host(self):
71 """If present, a string representing the host."""
72 try:
73 authority = self.authority_info()
74 except exc.InvalidAuthority:
75 return None
76 return authority["host"]
78 @property
79 def port(self):
80 """If present, the port extracted from the authority."""
81 try:
82 authority = self.authority_info()
83 except exc.InvalidAuthority:
84 return None
85 return authority["port"]
87 @property
88 def userinfo(self):
89 """If present, the userinfo extracted from the authority."""
90 try:
91 authority = self.authority_info()
92 except exc.InvalidAuthority:
93 return None
94 return authority["userinfo"]
96 def is_absolute(self):
97 """Determine if this URI Reference is an absolute URI.
99 See http://tools.ietf.org/html/rfc3986#section-4.3 for explanation.
101 :returns: ``True`` if it is an absolute URI, ``False`` otherwise.
102 :rtype: bool
103 """
104 return bool(misc.ABSOLUTE_URI_MATCHER.match(self.unsplit()))
106 def is_valid(self, **kwargs):
107 """Determine if the URI is valid.
109 .. deprecated:: 1.1.0
111 Use the :class:`~rfc3986.validators.Validator` object instead.
113 :param bool require_scheme: Set to ``True`` if you wish to require the
114 presence of the scheme component.
115 :param bool require_authority: Set to ``True`` if you wish to require
116 the presence of the authority component.
117 :param bool require_path: Set to ``True`` if you wish to require the
118 presence of the path component.
119 :param bool require_query: Set to ``True`` if you wish to require the
120 presence of the query component.
121 :param bool require_fragment: Set to ``True`` if you wish to require
122 the presence of the fragment component.
123 :returns: ``True`` if the URI is valid. ``False`` otherwise.
124 :rtype: bool
125 """
126 warnings.warn(
127 "Please use rfc3986.validators.Validator instead. "
128 "This method will be eventually removed.",
129 DeprecationWarning,
130 )
131 validators = [
132 (self.scheme_is_valid, kwargs.get("require_scheme", False)),
133 (self.authority_is_valid, kwargs.get("require_authority", False)),
134 (self.path_is_valid, kwargs.get("require_path", False)),
135 (self.query_is_valid, kwargs.get("require_query", False)),
136 (self.fragment_is_valid, kwargs.get("require_fragment", False)),
137 ]
138 return all(v(r) for v, r in validators)
140 def authority_is_valid(self, require=False):
141 """Determine if the authority component is valid.
143 .. deprecated:: 1.1.0
145 Use the :class:`~rfc3986.validators.Validator` object instead.
147 :param bool require:
148 Set to ``True`` to require the presence of this component.
149 :returns:
150 ``True`` if the authority is valid. ``False`` otherwise.
151 :rtype:
152 bool
153 """
154 warnings.warn(
155 "Please use rfc3986.validators.Validator instead. "
156 "This method will be eventually removed.",
157 DeprecationWarning,
158 )
159 try:
160 self.authority_info()
161 except exc.InvalidAuthority:
162 return False
164 return validators.authority_is_valid(
165 self.authority,
166 host=self.host,
167 require=require,
168 )
170 def scheme_is_valid(self, require=False):
171 """Determine if the scheme component is valid.
173 .. deprecated:: 1.1.0
175 Use the :class:`~rfc3986.validators.Validator` object instead.
177 :param str require: Set to ``True`` to require the presence of this
178 component.
179 :returns: ``True`` if the scheme is valid. ``False`` otherwise.
180 :rtype: bool
181 """
182 warnings.warn(
183 "Please use rfc3986.validators.Validator instead. "
184 "This method will be eventually removed.",
185 DeprecationWarning,
186 )
187 return validators.scheme_is_valid(self.scheme, require)
189 def path_is_valid(self, require=False):
190 """Determine if the path component is valid.
192 .. deprecated:: 1.1.0
194 Use the :class:`~rfc3986.validators.Validator` object instead.
196 :param str require: Set to ``True`` to require the presence of this
197 component.
198 :returns: ``True`` if the path is valid. ``False`` otherwise.
199 :rtype: bool
200 """
201 warnings.warn(
202 "Please use rfc3986.validators.Validator instead. "
203 "This method will be eventually removed.",
204 DeprecationWarning,
205 )
206 return validators.path_is_valid(self.path, require)
208 def query_is_valid(self, require=False):
209 """Determine if the query component is valid.
211 .. deprecated:: 1.1.0
213 Use the :class:`~rfc3986.validators.Validator` object instead.
215 :param str require: Set to ``True`` to require the presence of this
216 component.
217 :returns: ``True`` if the query is valid. ``False`` otherwise.
218 :rtype: bool
219 """
220 warnings.warn(
221 "Please use rfc3986.validators.Validator instead. "
222 "This method will be eventually removed.",
223 DeprecationWarning,
224 )
225 return validators.query_is_valid(self.query, require)
227 def fragment_is_valid(self, require=False):
228 """Determine if the fragment component is valid.
230 .. deprecated:: 1.1.0
232 Use the Validator object instead.
234 :param str require: Set to ``True`` to require the presence of this
235 component.
236 :returns: ``True`` if the fragment is valid. ``False`` otherwise.
237 :rtype: bool
238 """
239 warnings.warn(
240 "Please use rfc3986.validators.Validator instead. "
241 "This method will be eventually removed.",
242 DeprecationWarning,
243 )
244 return validators.fragment_is_valid(self.fragment, require)
246 def normalized_equality(self, other_ref):
247 """Compare this URIReference to another URIReference.
249 :param URIReference other_ref: (required), The reference with which
250 we're comparing.
251 :returns: ``True`` if the references are equal, ``False`` otherwise.
252 :rtype: bool
253 """
254 return tuple(self.normalize()) == tuple(other_ref.normalize())
256 def resolve_with(self, base_uri, strict=False):
257 """Use an absolute URI Reference to resolve this relative reference.
259 Assuming this is a relative reference that you would like to resolve,
260 use the provided base URI to resolve it.
262 See http://tools.ietf.org/html/rfc3986#section-5 for more information.
264 :param base_uri: Either a string or URIReference. It must be an
265 absolute URI or it will raise an exception.
266 :returns: A new URIReference which is the result of resolving this
267 reference using ``base_uri``.
268 :rtype: :class:`URIReference`
269 :raises rfc3986.exceptions.ResolutionError:
270 If the ``base_uri`` does not at least have a scheme.
271 """
272 if not isinstance(base_uri, URIMixin):
273 base_uri = type(self).from_string(base_uri)
275 try:
276 self._validator.validate(base_uri)
277 except exc.ValidationError:
278 raise exc.ResolutionError(base_uri)
280 # This is optional per
281 # http://tools.ietf.org/html/rfc3986#section-5.2.1
282 base_uri = base_uri.normalize()
284 # The reference we're resolving
285 resolving = self
287 if not strict and resolving.scheme == base_uri.scheme:
288 resolving = resolving.copy_with(scheme=None)
290 # http://tools.ietf.org/html/rfc3986#page-32
291 if resolving.scheme is not None:
292 target = resolving.copy_with(
293 path=normalizers.normalize_path(resolving.path)
294 )
295 else:
296 if resolving.authority is not None:
297 target = resolving.copy_with(
298 scheme=base_uri.scheme,
299 path=normalizers.normalize_path(resolving.path),
300 )
301 else:
302 if resolving.path is None:
303 if resolving.query is not None:
304 query = resolving.query
305 else:
306 query = base_uri.query
307 target = resolving.copy_with(
308 scheme=base_uri.scheme,
309 authority=base_uri.authority,
310 path=base_uri.path,
311 query=query,
312 )
313 else:
314 if resolving.path.startswith("/"):
315 path = normalizers.normalize_path(resolving.path)
316 else:
317 path = normalizers.normalize_path(
318 misc.merge_paths(base_uri, resolving.path)
319 )
320 target = resolving.copy_with(
321 scheme=base_uri.scheme,
322 authority=base_uri.authority,
323 path=path,
324 query=resolving.query,
325 )
326 return target
328 def unsplit(self):
329 """Create a URI string from the components.
331 :returns: The URI Reference reconstituted as a string.
332 :rtype: str
333 """
334 # See http://tools.ietf.org/html/rfc3986#section-5.3
335 result_list = []
336 if self.scheme:
337 result_list.extend([self.scheme, ":"])
338 if self.authority:
339 result_list.extend(["//", self.authority])
340 if self.path:
341 result_list.append(self.path)
342 if self.query is not None:
343 result_list.extend(["?", self.query])
344 if self.fragment is not None:
345 result_list.extend(["#", self.fragment])
346 return "".join(result_list)
348 def copy_with(
349 self,
350 scheme=misc.UseExisting,
351 authority=misc.UseExisting,
352 path=misc.UseExisting,
353 query=misc.UseExisting,
354 fragment=misc.UseExisting,
355 ):
356 """Create a copy of this reference with the new components.
358 :param str scheme:
359 (optional) The scheme to use for the new reference.
360 :param str authority:
361 (optional) The authority to use for the new reference.
362 :param str path:
363 (optional) The path to use for the new reference.
364 :param str query:
365 (optional) The query to use for the new reference.
366 :param str fragment:
367 (optional) The fragment to use for the new reference.
368 :returns:
369 New URIReference with provided components.
370 :rtype:
371 URIReference
372 """
373 attributes = {
374 "scheme": scheme,
375 "authority": authority,
376 "path": path,
377 "query": query,
378 "fragment": fragment,
379 }
380 for key, value in list(attributes.items()):
381 if value is misc.UseExisting:
382 del attributes[key]
383 uri = self._replace(**attributes)
384 uri.encoding = self.encoding
385 return uri