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