1 | ===============
|
---|
2 | Building URIs
|
---|
3 | ===============
|
---|
4 |
|
---|
5 | Constructing URLs often seems simple. There are some problems with
|
---|
6 | concatenating strings to build a URL:
|
---|
7 |
|
---|
8 | - Certain parts of the URL disallow certain characters
|
---|
9 |
|
---|
10 | - Formatting some parts of the URL is tricky and doing it manually isn't fun
|
---|
11 |
|
---|
12 | To make the experience better |rfc3986| provides the
|
---|
13 | :class:`~rfc3986.builder.URIBuilder` class to generate valid
|
---|
14 | :class:`~rfc3986.uri.URIReference` instances. The
|
---|
15 | :class:`~rfc3986.builder.URIBuilder` class will handle ensuring that each
|
---|
16 | component is normalized and safe for real world use.
|
---|
17 |
|
---|
18 |
|
---|
19 | Example Usage
|
---|
20 | =============
|
---|
21 |
|
---|
22 | .. note::
|
---|
23 |
|
---|
24 | All of the methods on a :class:`~rfc3986.builder.URIBuilder` are
|
---|
25 | chainable (except :meth:`~rfc3986.builder.URIBuilder.finalize` and
|
---|
26 | :meth:`~rfc3986.builder.URIBuilder.geturl` as neither returns a
|
---|
27 | :class:`~rfc3986.builder.URIBuilder`).
|
---|
28 |
|
---|
29 | Building From Scratch
|
---|
30 | ---------------------
|
---|
31 |
|
---|
32 | Let's build a basic URL with just a scheme and host. First we create an
|
---|
33 | instance of :class:`~rfc3986.builder.URIBuilder`. Then we call
|
---|
34 | :meth:`~rfc3986.builder.URIBuilder.add_scheme` and
|
---|
35 | :meth:`~rfc3986.builder.URIBuilder.add_host` with the scheme and host
|
---|
36 | we want to include in the URL. Then we convert our builder object into
|
---|
37 | a :class:`~rfc3986.uri.URIReference` and call
|
---|
38 | :meth:`~rfc3986.uri.URIReference.unsplit`.
|
---|
39 |
|
---|
40 | .. doctest::
|
---|
41 |
|
---|
42 | >>> from rfc3986 import builder
|
---|
43 | >>> print(builder.URIBuilder().add_scheme(
|
---|
44 | ... 'https'
|
---|
45 | ... ).add_host(
|
---|
46 | ... 'github.com'
|
---|
47 | ... ).finalize().unsplit())
|
---|
48 | https://github.com
|
---|
49 |
|
---|
50 |
|
---|
51 | Replacing Components of a URI
|
---|
52 | -----------------------------
|
---|
53 |
|
---|
54 | It is possible to update an existing URI by constructing a builder from an
|
---|
55 | instance of :class:`~rfc3986.uri.URIReference` or a textual representation:
|
---|
56 |
|
---|
57 | .. doctest::
|
---|
58 |
|
---|
59 | >>> from rfc3986 import builder
|
---|
60 | >>> print(builder.URIBuilder.from_uri("http://github.com").add_scheme(
|
---|
61 | ... 'https'
|
---|
62 | ... ).finalize().unsplit())
|
---|
63 | https://github.com
|
---|
64 |
|
---|
65 | The Builder is Immutable
|
---|
66 | ------------------------
|
---|
67 |
|
---|
68 | Each time you invoke a method, you get a new instance of a
|
---|
69 | :class:`~rfc3986.builder.URIBuilder` class so you can build several different
|
---|
70 | URLs from one base instance.
|
---|
71 |
|
---|
72 | .. doctest::
|
---|
73 |
|
---|
74 | >>> from rfc3986 import builder
|
---|
75 | >>> github_builder = builder.URIBuilder().add_scheme(
|
---|
76 | ... 'https'
|
---|
77 | ... ).add_host(
|
---|
78 | ... 'api.github.com'
|
---|
79 | ... )
|
---|
80 | >>> print(github_builder.add_path(
|
---|
81 | ... '/users/sigmavirus24'
|
---|
82 | ... ).finalize().unsplit())
|
---|
83 | https://api.github.com/users/sigmavirus24
|
---|
84 | >>> print(github_builder.add_path(
|
---|
85 | ... '/repos/sigmavirus24/rfc3986'
|
---|
86 | ... ).finalize().unsplit())
|
---|
87 | https://api.github.com/repos/sigmavirus24/rfc3986
|
---|
88 |
|
---|
89 | Convenient Path Management
|
---|
90 | --------------------------
|
---|
91 |
|
---|
92 | Because our builder is immutable, one could use the
|
---|
93 | :class:`~rfc3986.builder.URIBuilder` class to build a class to make HTTP
|
---|
94 | Requests that used the provided path to extend the original one.
|
---|
95 |
|
---|
96 | .. doctest::
|
---|
97 |
|
---|
98 | >>> from rfc3986 import builder
|
---|
99 | >>> github_builder = builder.URIBuilder().add_scheme(
|
---|
100 | ... 'https'
|
---|
101 | ... ).add_host(
|
---|
102 | ... 'api.github.com'
|
---|
103 | ... ).add_path(
|
---|
104 | ... '/users'
|
---|
105 | ... )
|
---|
106 | >>> print(github_builder.extend_path("sigmavirus24").geturl())
|
---|
107 | https://api.github.com/users/sigmavirus24
|
---|
108 | >>> print(github_builder.extend_path("lukasa").geturl())
|
---|
109 | https://api.github.com/users/lukasa
|
---|
110 |
|
---|
111 |
|
---|
112 | Convenient Credential Handling
|
---|
113 | ------------------------------
|
---|
114 |
|
---|
115 | |rfc3986| makes adding authentication credentials convenient. It takes care of
|
---|
116 | making the credentials URL safe. There are some characters someone might want
|
---|
117 | to include in a URL that are not safe for the authority component of a URL.
|
---|
118 |
|
---|
119 | .. doctest::
|
---|
120 |
|
---|
121 | >>> from rfc3986 import builder
|
---|
122 | >>> print(builder.URIBuilder().add_scheme(
|
---|
123 | ... 'https'
|
---|
124 | ... ).add_host(
|
---|
125 | ... 'api.github.com'
|
---|
126 | ... ).add_credentials(
|
---|
127 | ... username='us3r',
|
---|
128 | ... password='p@ssw0rd',
|
---|
129 | ... ).finalize().unsplit())
|
---|
130 | https://us3r:p%40ssw0rd@api.github.com
|
---|
131 |
|
---|
132 | Managing Query String Parameters
|
---|
133 | --------------------------------
|
---|
134 |
|
---|
135 | Further, |rfc3986| attempts to simplify the process of adding query parameters
|
---|
136 | to a URL. For example, if we were using Elasticsearch, we might do something
|
---|
137 | like:
|
---|
138 |
|
---|
139 | .. doctest::
|
---|
140 |
|
---|
141 | >>> from rfc3986 import builder
|
---|
142 | >>> print(builder.URIBuilder().add_scheme(
|
---|
143 | ... 'https'
|
---|
144 | ... ).add_host(
|
---|
145 | ... 'search.example.com'
|
---|
146 | ... ).add_path(
|
---|
147 | ... '_search'
|
---|
148 | ... ).add_query_from(
|
---|
149 | ... [('q', 'repo:sigmavirus24/rfc3986'), ('sort', 'created_at:asc')]
|
---|
150 | ... ).finalize().unsplit())
|
---|
151 | https://search.example.com/_search?q=repo%3Asigmavirus24%2Frfc3986&sort=created_at%3Aasc
|
---|
152 |
|
---|
153 | If one also had an existing URL with query string that we merely wanted to
|
---|
154 | append to, we can also do that with |rfc3986|.
|
---|
155 |
|
---|
156 | .. doctest::
|
---|
157 |
|
---|
158 | >>> from rfc3986 import builder
|
---|
159 | >>> print(builder.URIBuilder().from_uri(
|
---|
160 | ... 'https://search.example.com/_search?q=repo%3Asigmavirus24%2Frfc3986'
|
---|
161 | ... ).extend_query_with(
|
---|
162 | ... [('sort', 'created_at:asc')]
|
---|
163 | ... ).finalize().unsplit())
|
---|
164 | https://search.example.com/_search?q=repo%3Asigmavirus24%2Frfc3986&sort=created_at%3Aasc
|
---|
165 |
|
---|
166 | Adding Fragments
|
---|
167 | ----------------
|
---|
168 |
|
---|
169 | Finally, we provide a way to add a fragment to a URL. Let's build up a URL to
|
---|
170 | view the section of the RFC that refers to fragments:
|
---|
171 |
|
---|
172 | .. doctest::
|
---|
173 |
|
---|
174 | >>> from rfc3986 import builder
|
---|
175 | >>> print(builder.URIBuilder().add_scheme(
|
---|
176 | ... 'https'
|
---|
177 | ... ).add_host(
|
---|
178 | ... 'tools.ietf.org'
|
---|
179 | ... ).add_path(
|
---|
180 | ... '/html/rfc3986'
|
---|
181 | ... ).add_fragment(
|
---|
182 | ... 'section-3.5'
|
---|
183 | ... ).finalize().unsplit())
|
---|
184 | https://tools.ietf.org/html/rfc3986#section-3.5
|
---|