| """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 |