|
- """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)
-
- @property
- def host(self):
- """If present, a string representing the host."""
- try:
- authority = self.authority_info()
- except exc.InvalidAuthority:
- return None
- return authority['host']
-
- @property
- def port(self):
- """If present, the port extracted from the authority."""
- try:
- authority = self.authority_info()
- except exc.InvalidAuthority:
- return None
- return authority['port']
-
- @property
- 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
|