[230] | 1 | """Module containing the implementation of the URIReference class."""
|
---|
| 2 | # -*- coding: utf-8 -*-
|
---|
| 3 | # Copyright (c) 2014 Rackspace
|
---|
| 4 | # Copyright (c) 2015 Ian Stapleton Cordasco
|
---|
| 5 | # Licensed under the Apache License, Version 2.0 (the "License");
|
---|
| 6 | # you may not use this file except in compliance with the License.
|
---|
| 7 | # You may obtain a copy of the License at
|
---|
| 8 | #
|
---|
| 9 | # http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 10 | #
|
---|
| 11 | # Unless required by applicable law or agreed to in writing, software
|
---|
| 12 | # distributed under the License is distributed on an "AS IS" BASIS,
|
---|
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
---|
| 14 | # implied.
|
---|
| 15 | # See the License for the specific language governing permissions and
|
---|
| 16 | # limitations under the License.
|
---|
| 17 | from collections import namedtuple
|
---|
| 18 |
|
---|
| 19 | from . import compat
|
---|
| 20 | from . import misc
|
---|
| 21 | from . import normalizers
|
---|
| 22 | from ._mixin import URIMixin
|
---|
| 23 |
|
---|
| 24 |
|
---|
| 25 | class URIReference(namedtuple("URIReference", misc.URI_COMPONENTS), URIMixin):
|
---|
| 26 | """Immutable object representing a parsed URI Reference.
|
---|
| 27 |
|
---|
| 28 | .. note::
|
---|
| 29 |
|
---|
| 30 | This class is not intended to be directly instantiated by the user.
|
---|
| 31 |
|
---|
| 32 | This object exposes attributes for the following components of a
|
---|
| 33 | URI:
|
---|
| 34 |
|
---|
| 35 | - scheme
|
---|
| 36 | - authority
|
---|
| 37 | - path
|
---|
| 38 | - query
|
---|
| 39 | - fragment
|
---|
| 40 |
|
---|
| 41 | .. attribute:: scheme
|
---|
| 42 |
|
---|
| 43 | The scheme that was parsed for the URI Reference. For example,
|
---|
| 44 | ``http``, ``https``, ``smtp``, ``imap``, etc.
|
---|
| 45 |
|
---|
| 46 | .. attribute:: authority
|
---|
| 47 |
|
---|
| 48 | Component of the URI that contains the user information, host,
|
---|
| 49 | and port sub-components. For example,
|
---|
| 50 | ``google.com``, ``127.0.0.1:5000``, ``username@[::1]``,
|
---|
| 51 | ``username:password@example.com:443``, etc.
|
---|
| 52 |
|
---|
| 53 | .. attribute:: path
|
---|
| 54 |
|
---|
| 55 | The path that was parsed for the given URI Reference. For example,
|
---|
| 56 | ``/``, ``/index.php``, etc.
|
---|
| 57 |
|
---|
| 58 | .. attribute:: query
|
---|
| 59 |
|
---|
| 60 | The query component for a given URI Reference. For example, ``a=b``,
|
---|
| 61 | ``a=b%20c``, ``a=b+c``, ``a=b,c=d,e=%20f``, etc.
|
---|
| 62 |
|
---|
| 63 | .. attribute:: fragment
|
---|
| 64 |
|
---|
| 65 | The fragment component of a URI. For example, ``section-3.1``.
|
---|
| 66 |
|
---|
| 67 | This class also provides extra attributes for easier access to information
|
---|
| 68 | like the subcomponents of the authority component.
|
---|
| 69 |
|
---|
| 70 | .. attribute:: userinfo
|
---|
| 71 |
|
---|
| 72 | The user information parsed from the authority.
|
---|
| 73 |
|
---|
| 74 | .. attribute:: host
|
---|
| 75 |
|
---|
| 76 | The hostname, IPv4, or IPv6 address parsed from the authority.
|
---|
| 77 |
|
---|
| 78 | .. attribute:: port
|
---|
| 79 |
|
---|
| 80 | The port parsed from the authority.
|
---|
| 81 | """
|
---|
| 82 |
|
---|
| 83 | slots = ()
|
---|
| 84 |
|
---|
| 85 | def __new__(
|
---|
| 86 | cls, scheme, authority, path, query, fragment, encoding="utf-8"
|
---|
| 87 | ):
|
---|
| 88 | """Create a new URIReference."""
|
---|
| 89 | ref = super(URIReference, cls).__new__(
|
---|
| 90 | cls,
|
---|
| 91 | scheme or None,
|
---|
| 92 | authority or None,
|
---|
| 93 | path or None,
|
---|
| 94 | query,
|
---|
| 95 | fragment,
|
---|
| 96 | )
|
---|
| 97 | ref.encoding = encoding
|
---|
| 98 | return ref
|
---|
| 99 |
|
---|
| 100 | __hash__ = tuple.__hash__
|
---|
| 101 |
|
---|
| 102 | def __eq__(self, other):
|
---|
| 103 | """Compare this reference to another."""
|
---|
| 104 | other_ref = other
|
---|
| 105 | if isinstance(other, tuple):
|
---|
| 106 | other_ref = URIReference(*other)
|
---|
| 107 | elif not isinstance(other, URIReference):
|
---|
| 108 | try:
|
---|
| 109 | other_ref = URIReference.from_string(other)
|
---|
| 110 | except TypeError:
|
---|
| 111 | raise TypeError(
|
---|
| 112 | "Unable to compare URIReference() to {0}()".format(
|
---|
| 113 | type(other).__name__
|
---|
| 114 | )
|
---|
| 115 | )
|
---|
| 116 |
|
---|
| 117 | # See http://tools.ietf.org/html/rfc3986#section-6.2
|
---|
| 118 | naive_equality = tuple(self) == tuple(other_ref)
|
---|
| 119 | return naive_equality or self.normalized_equality(other_ref)
|
---|
| 120 |
|
---|
| 121 | def normalize(self):
|
---|
| 122 | """Normalize this reference as described in Section 6.2.2.
|
---|
| 123 |
|
---|
| 124 | This is not an in-place normalization. Instead this creates a new
|
---|
| 125 | URIReference.
|
---|
| 126 |
|
---|
| 127 | :returns: A new reference object with normalized components.
|
---|
| 128 | :rtype: URIReference
|
---|
| 129 | """
|
---|
| 130 | # See http://tools.ietf.org/html/rfc3986#section-6.2.2 for logic in
|
---|
| 131 | # this method.
|
---|
| 132 | return URIReference(
|
---|
| 133 | normalizers.normalize_scheme(self.scheme or ""),
|
---|
| 134 | normalizers.normalize_authority(
|
---|
| 135 | (self.userinfo, self.host, self.port)
|
---|
| 136 | ),
|
---|
| 137 | normalizers.normalize_path(self.path or ""),
|
---|
| 138 | normalizers.normalize_query(self.query),
|
---|
| 139 | normalizers.normalize_fragment(self.fragment),
|
---|
| 140 | self.encoding,
|
---|
| 141 | )
|
---|
| 142 |
|
---|
| 143 | @classmethod
|
---|
| 144 | def from_string(cls, uri_string, encoding="utf-8"):
|
---|
| 145 | """Parse a URI reference from the given unicode URI string.
|
---|
| 146 |
|
---|
| 147 | :param str uri_string: Unicode URI to be parsed into a reference.
|
---|
| 148 | :param str encoding: The encoding of the string provided
|
---|
| 149 | :returns: :class:`URIReference` or subclass thereof
|
---|
| 150 | """
|
---|
| 151 | uri_string = compat.to_str(uri_string, encoding)
|
---|
| 152 |
|
---|
| 153 | split_uri = misc.URI_MATCHER.match(uri_string).groupdict()
|
---|
| 154 | return cls(
|
---|
| 155 | split_uri["scheme"],
|
---|
| 156 | split_uri["authority"],
|
---|
| 157 | normalizers.encode_component(split_uri["path"], encoding),
|
---|
| 158 | normalizers.encode_component(split_uri["query"], encoding),
|
---|
| 159 | normalizers.encode_component(split_uri["fragment"], encoding),
|
---|
| 160 | encoding,
|
---|
| 161 | )
|
---|