Spaces:
Runtime error
Runtime error
"""Module containing the implementation of the URIMixin class.""" | |
import warnings | |
from . import exceptions as exc | |
from . import misc | |
from . import normalizers | |
from . import validators | |
class URIMixin(object): | |
"""Mixin with all shared methods for URIs and IRIs.""" | |
__hash__ = tuple.__hash__ | |
def authority_info(self): | |
"""Return a dictionary with the ``userinfo``, ``host``, and ``port``. | |
If the authority is not valid, it will raise a | |
:class:`~rfc3986.exceptions.InvalidAuthority` Exception. | |
:returns: | |
``{'userinfo': 'username:password', 'host': 'www.example.com', | |
'port': '80'}`` | |
:rtype: dict | |
:raises rfc3986.exceptions.InvalidAuthority: | |
If the authority is not ``None`` and can not be parsed. | |
""" | |
if not self.authority: | |
return {"userinfo": None, "host": None, "port": None} | |
match = self._match_subauthority() | |
if match is None: | |
# In this case, we have an authority that was parsed from the URI | |
# Reference, but it cannot be further parsed by our | |
# misc.SUBAUTHORITY_MATCHER. In this case it must not be a valid | |
# authority. | |
raise exc.InvalidAuthority(self.authority.encode(self.encoding)) | |
# We had a match, now let's ensure that it is actually a valid host | |
# address if it is IPv4 | |
matches = match.groupdict() | |
host = matches.get("host") | |
if ( | |
host | |
and misc.IPv4_MATCHER.match(host) | |
and not validators.valid_ipv4_host_address(host) | |
): | |
# If we have a host, it appears to be IPv4 and it does not have | |
# valid bytes, it is an InvalidAuthority. | |
raise exc.InvalidAuthority(self.authority.encode(self.encoding)) | |
return matches | |
def _match_subauthority(self): | |
return misc.SUBAUTHORITY_MATCHER.match(self.authority) | |
def host(self): | |
"""If present, a string representing the host.""" | |
try: | |
authority = self.authority_info() | |
except exc.InvalidAuthority: | |
return None | |
return authority["host"] | |
def port(self): | |
"""If present, the port extracted from the authority.""" | |
try: | |
authority = self.authority_info() | |
except exc.InvalidAuthority: | |
return None | |
return authority["port"] | |
def userinfo(self): | |
"""If present, the userinfo extracted from the authority.""" | |
try: | |
authority = self.authority_info() | |
except exc.InvalidAuthority: | |
return None | |
return authority["userinfo"] | |
def is_absolute(self): | |
"""Determine if this URI Reference is an absolute URI. | |
See http://tools.ietf.org/html/rfc3986#section-4.3 for explanation. | |
:returns: ``True`` if it is an absolute URI, ``False`` otherwise. | |
:rtype: bool | |
""" | |
return bool(misc.ABSOLUTE_URI_MATCHER.match(self.unsplit())) | |
def is_valid(self, **kwargs): | |
"""Determine if the URI is valid. | |
.. deprecated:: 1.1.0 | |
Use the :class:`~rfc3986.validators.Validator` object instead. | |
:param bool require_scheme: Set to ``True`` if you wish to require the | |
presence of the scheme component. | |
:param bool require_authority: Set to ``True`` if you wish to require | |
the presence of the authority component. | |
:param bool require_path: Set to ``True`` if you wish to require the | |
presence of the path component. | |
:param bool require_query: Set to ``True`` if you wish to require the | |
presence of the query component. | |
:param bool require_fragment: Set to ``True`` if you wish to require | |
the presence of the fragment component. | |
:returns: ``True`` if the URI is valid. ``False`` otherwise. | |
:rtype: bool | |
""" | |
warnings.warn( | |
"Please use rfc3986.validators.Validator instead. " | |
"This method will be eventually removed.", | |
DeprecationWarning, | |
) | |
validators = [ | |
(self.scheme_is_valid, kwargs.get("require_scheme", False)), | |
(self.authority_is_valid, kwargs.get("require_authority", False)), | |
(self.path_is_valid, kwargs.get("require_path", False)), | |
(self.query_is_valid, kwargs.get("require_query", False)), | |
(self.fragment_is_valid, kwargs.get("require_fragment", False)), | |
] | |
return all(v(r) for v, r in validators) | |
def authority_is_valid(self, require=False): | |
"""Determine if the authority component is valid. | |
.. deprecated:: 1.1.0 | |
Use the :class:`~rfc3986.validators.Validator` object instead. | |
:param bool require: | |
Set to ``True`` to require the presence of this component. | |
:returns: | |
``True`` if the authority is valid. ``False`` otherwise. | |
:rtype: | |
bool | |
""" | |
warnings.warn( | |
"Please use rfc3986.validators.Validator instead. " | |
"This method will be eventually removed.", | |
DeprecationWarning, | |
) | |
try: | |
self.authority_info() | |
except exc.InvalidAuthority: | |
return False | |
return validators.authority_is_valid( | |
self.authority, | |
host=self.host, | |
require=require, | |
) | |
def scheme_is_valid(self, require=False): | |
"""Determine if the scheme component is valid. | |
.. deprecated:: 1.1.0 | |
Use the :class:`~rfc3986.validators.Validator` object instead. | |
:param str require: Set to ``True`` to require the presence of this | |
component. | |
:returns: ``True`` if the scheme is valid. ``False`` otherwise. | |
:rtype: bool | |
""" | |
warnings.warn( | |
"Please use rfc3986.validators.Validator instead. " | |
"This method will be eventually removed.", | |
DeprecationWarning, | |
) | |
return validators.scheme_is_valid(self.scheme, require) | |
def path_is_valid(self, require=False): | |
"""Determine if the path component is valid. | |
.. deprecated:: 1.1.0 | |
Use the :class:`~rfc3986.validators.Validator` object instead. | |
:param str require: Set to ``True`` to require the presence of this | |
component. | |
:returns: ``True`` if the path is valid. ``False`` otherwise. | |
:rtype: bool | |
""" | |
warnings.warn( | |
"Please use rfc3986.validators.Validator instead. " | |
"This method will be eventually removed.", | |
DeprecationWarning, | |
) | |
return validators.path_is_valid(self.path, require) | |
def query_is_valid(self, require=False): | |
"""Determine if the query component is valid. | |
.. deprecated:: 1.1.0 | |
Use the :class:`~rfc3986.validators.Validator` object instead. | |
:param str require: Set to ``True`` to require the presence of this | |
component. | |
:returns: ``True`` if the query is valid. ``False`` otherwise. | |
:rtype: bool | |
""" | |
warnings.warn( | |
"Please use rfc3986.validators.Validator instead. " | |
"This method will be eventually removed.", | |
DeprecationWarning, | |
) | |
return validators.query_is_valid(self.query, require) | |
def fragment_is_valid(self, require=False): | |
"""Determine if the fragment component is valid. | |
.. deprecated:: 1.1.0 | |
Use the Validator object instead. | |
:param str require: Set to ``True`` to require the presence of this | |
component. | |
:returns: ``True`` if the fragment is valid. ``False`` otherwise. | |
:rtype: bool | |
""" | |
warnings.warn( | |
"Please use rfc3986.validators.Validator instead. " | |
"This method will be eventually removed.", | |
DeprecationWarning, | |
) | |
return validators.fragment_is_valid(self.fragment, require) | |
def normalized_equality(self, other_ref): | |
"""Compare this URIReference to another URIReference. | |
:param URIReference other_ref: (required), The reference with which | |
we're comparing. | |
:returns: ``True`` if the references are equal, ``False`` otherwise. | |
:rtype: bool | |
""" | |
return tuple(self.normalize()) == tuple(other_ref.normalize()) | |
def resolve_with(self, base_uri, strict=False): | |
"""Use an absolute URI Reference to resolve this relative reference. | |
Assuming this is a relative reference that you would like to resolve, | |
use the provided base URI to resolve it. | |
See http://tools.ietf.org/html/rfc3986#section-5 for more information. | |
:param base_uri: Either a string or URIReference. It must be an | |
absolute URI or it will raise an exception. | |
:returns: A new URIReference which is the result of resolving this | |
reference using ``base_uri``. | |
:rtype: :class:`URIReference` | |
:raises rfc3986.exceptions.ResolutionError: | |
If the ``base_uri`` is not an absolute URI. | |
""" | |
if not isinstance(base_uri, URIMixin): | |
base_uri = type(self).from_string(base_uri) | |
if not base_uri.is_absolute(): | |
raise exc.ResolutionError(base_uri) | |
# This is optional per | |
# http://tools.ietf.org/html/rfc3986#section-5.2.1 | |
base_uri = base_uri.normalize() | |
# The reference we're resolving | |
resolving = self | |
if not strict and resolving.scheme == base_uri.scheme: | |
resolving = resolving.copy_with(scheme=None) | |
# http://tools.ietf.org/html/rfc3986#page-32 | |
if resolving.scheme is not None: | |
target = resolving.copy_with( | |
path=normalizers.normalize_path(resolving.path) | |
) | |
else: | |
if resolving.authority is not None: | |
target = resolving.copy_with( | |
scheme=base_uri.scheme, | |
path=normalizers.normalize_path(resolving.path), | |
) | |
else: | |
if resolving.path is None: | |
if resolving.query is not None: | |
query = resolving.query | |
else: | |
query = base_uri.query | |
target = resolving.copy_with( | |
scheme=base_uri.scheme, | |
authority=base_uri.authority, | |
path=base_uri.path, | |
query=query, | |
) | |
else: | |
if resolving.path.startswith("/"): | |
path = normalizers.normalize_path(resolving.path) | |
else: | |
path = normalizers.normalize_path( | |
misc.merge_paths(base_uri, resolving.path) | |
) | |
target = resolving.copy_with( | |
scheme=base_uri.scheme, | |
authority=base_uri.authority, | |
path=path, | |
query=resolving.query, | |
) | |
return target | |
def unsplit(self): | |
"""Create a URI string from the components. | |
:returns: The URI Reference reconstituted as a string. | |
:rtype: str | |
""" | |
# See http://tools.ietf.org/html/rfc3986#section-5.3 | |
result_list = [] | |
if self.scheme: | |
result_list.extend([self.scheme, ":"]) | |
if self.authority: | |
result_list.extend(["//", self.authority]) | |
if self.path: | |
result_list.append(self.path) | |
if self.query is not None: | |
result_list.extend(["?", self.query]) | |
if self.fragment is not None: | |
result_list.extend(["#", self.fragment]) | |
return "".join(result_list) | |
def copy_with( | |
self, | |
scheme=misc.UseExisting, | |
authority=misc.UseExisting, | |
path=misc.UseExisting, | |
query=misc.UseExisting, | |
fragment=misc.UseExisting, | |
): | |
"""Create a copy of this reference with the new components. | |
:param str scheme: | |
(optional) The scheme to use for the new reference. | |
:param str authority: | |
(optional) The authority to use for the new reference. | |
:param str path: | |
(optional) The path to use for the new reference. | |
:param str query: | |
(optional) The query to use for the new reference. | |
:param str fragment: | |
(optional) The fragment to use for the new reference. | |
:returns: | |
New URIReference with provided components. | |
:rtype: | |
URIReference | |
""" | |
attributes = { | |
"scheme": scheme, | |
"authority": authority, | |
"path": path, | |
"query": query, | |
"fragment": fragment, | |
} | |
for key, value in list(attributes.items()): | |
if value is misc.UseExisting: | |
del attributes[key] | |
uri = self._replace(**attributes) | |
uri.encoding = self.encoding | |
return uri | |