|
- """Module containing the implementation of the IRIReference class."""
- # -*- coding: utf-8 -*-
- # Copyright (c) 2014 Rackspace
- # Copyright (c) 2015 Ian Stapleton Cordasco
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- # implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from collections import namedtuple
-
- from . import compat
- from . import exceptions
- from . import misc
- from . import normalizers
- from . import uri
-
-
- try:
- import idna
- except ImportError: # pragma: no cover
- idna = None
-
-
- class IRIReference(namedtuple('IRIReference', misc.URI_COMPONENTS),
- uri.URIMixin):
- """Immutable object representing a parsed IRI Reference.
-
- Can be encoded into an URIReference object via the procedure
- specified in RFC 3987 Section 3.1
-
- .. note::
- The IRI submodule is a new interface and may possibly change in
- the future. Check for changes to the interface when upgrading.
- """
-
- slots = ()
-
- def __new__(cls, scheme, authority, path, query, fragment,
- encoding='utf-8'):
- """Create a new IRIReference."""
- ref = super(IRIReference, cls).__new__(
- cls,
- scheme or None,
- authority or None,
- path or None,
- query,
- fragment)
- ref.encoding = encoding
- return ref
-
- def __eq__(self, other):
- """Compare this reference to another."""
- other_ref = other
- if isinstance(other, tuple):
- other_ref = self.__class__(*other)
- elif not isinstance(other, IRIReference):
- try:
- other_ref = self.__class__.from_string(other)
- except TypeError:
- raise TypeError(
- 'Unable to compare {0}() to {1}()'.format(
- type(self).__name__, type(other).__name__))
-
- # See http://tools.ietf.org/html/rfc3986#section-6.2
- return tuple(self) == tuple(other_ref)
-
- def _match_subauthority(self):
- return misc.ISUBAUTHORITY_MATCHER.match(self.authority)
-
- @classmethod
- def from_string(cls, iri_string, encoding='utf-8'):
- """Parse a IRI reference from the given unicode IRI string.
-
- :param str iri_string: Unicode IRI to be parsed into a reference.
- :param str encoding: The encoding of the string provided
- :returns: :class:`IRIReference` or subclass thereof
- """
- iri_string = compat.to_str(iri_string, encoding)
-
- split_iri = misc.IRI_MATCHER.match(iri_string).groupdict()
- return cls(
- split_iri['scheme'], split_iri['authority'],
- normalizers.encode_component(split_iri['path'], encoding),
- normalizers.encode_component(split_iri['query'], encoding),
- normalizers.encode_component(split_iri['fragment'], encoding),
- encoding,
- )
-
- def encode(self, idna_encoder=None): # noqa: C901
- """Encode an IRIReference into a URIReference instance.
-
- If the ``idna`` module is installed or the ``rfc3986[idna]``
- extra is used then unicode characters in the IRI host
- component will be encoded with IDNA2008.
-
- :param idna_encoder:
- Function that encodes each part of the host component
- If not given will raise an exception if the IRI
- contains a host component.
- :rtype: uri.URIReference
- :returns: A URI reference
- """
- authority = self.authority
- if authority:
- if idna_encoder is None:
- if idna is None: # pragma: no cover
- raise exceptions.MissingDependencyError(
- "Could not import the 'idna' module "
- "and the IRI hostname requires encoding"
- )
-
- def idna_encoder(name):
- if any(ord(c) > 128 for c in name):
- try:
- return idna.encode(name.lower(),
- strict=True,
- std3_rules=True)
- except idna.IDNAError:
- raise exceptions.InvalidAuthority(self.authority)
- return name
-
- authority = ""
- if self.host:
- authority = ".".join([compat.to_str(idna_encoder(part))
- for part in self.host.split(".")])
-
- if self.userinfo is not None:
- authority = (normalizers.encode_component(
- self.userinfo, self.encoding) + '@' + authority)
-
- if self.port is not None:
- authority += ":" + str(self.port)
-
- return uri.URIReference(self.scheme,
- authority,
- path=self.path,
- query=self.query,
- fragment=self.fragment,
- encoding=self.encoding)
|