Merge ~cjwatson/launchpadlib:py3-doctests into launchpadlib:main
- Git
- lp:~cjwatson/launchpadlib
- py3-doctests
- Merge into main
Proposed by
Colin Watson
Status: | Merged |
---|---|
Merged at revision: | a9134e386c3957b5ffc27eb1d499e8c5e9a74c72 |
Proposed branch: | ~cjwatson/launchpadlib:py3-doctests |
Merge into: | launchpadlib:main |
Diff against target: |
650 lines (+113/-111) 6 files modified
NEWS.rst (+1/-0) src/launchpadlib/docs/command-line.rst (+2/-2) src/launchpadlib/docs/hosted-files.rst (+10/-10) src/launchpadlib/docs/introduction.rst (+36/-35) src/launchpadlib/docs/people.rst (+45/-45) src/launchpadlib/docs/toplevel.rst (+19/-19) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrey Fedoseev (community) | Approve | ||
Jürgen Gmach | Approve | ||
Review via email: mp+425086@code.launchpad.net |
Commit message
Fix doctests for Python 3
Description of the change
The doctests are currently only run via Launchpad's test suite, and we'd accidentally not been running them since renaming from `.txt` to `.rst` in 1.10.8.
To post a comment you must log in.
Revision history for this message
Andrey Fedoseev (andrey-fedoseev) wrote : | # |
LGTM
> we'd accidentally not been running them since renaming from `.txt` to `.rst` in 1.10.8.
Shall we be running them from now on?
review:
Approve
Revision history for this message
Colin Watson (cjwatson) wrote : | # |
Indeed, at the moment we can only run these tests by using Launchpad itself as a test fixture.
We'll be running them again once we upgrade Launchpad to this version of launchpadlib. I'll get on that!
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/NEWS.rst b/NEWS.rst | |||
2 | index ac72636..d1ab45a 100644 | |||
3 | --- a/NEWS.rst | |||
4 | +++ b/NEWS.rst | |||
5 | @@ -6,6 +6,7 @@ NEWS for launchpadlib | |||
6 | 6 | ==================== | 6 | ==================== |
7 | 7 | - Generate coverage report. | 7 | - Generate coverage report. |
8 | 8 | - Use ``pytest`` as test runner. | 8 | - Use ``pytest`` as test runner. |
9 | 9 | - Fix doctests for Python 3. | ||
10 | 9 | 10 | ||
11 | 10 | 1.10.16 (2022-01-21) | 11 | 1.10.16 (2022-01-21) |
12 | 11 | ==================== | 12 | ==================== |
13 | diff --git a/src/launchpadlib/docs/command-line.rst b/src/launchpadlib/docs/command-line.rst | |||
14 | index 8fd0ef8..4f8d6e9 100644 | |||
15 | --- a/src/launchpadlib/docs/command-line.rst | |||
16 | +++ b/src/launchpadlib/docs/command-line.rst | |||
17 | @@ -33,8 +33,8 @@ token and the available access levels. | |||
18 | 33 | oauth_token_consumer | 33 | oauth_token_consumer |
19 | 34 | oauth_token_secret | 34 | oauth_token_secret |
20 | 35 | 35 | ||
22 | 36 | >>> print token_json['lp.context'] | 36 | >>> print(token_json['lp.context']) |
23 | 37 | context | 37 | context |
24 | 38 | 38 | ||
26 | 39 | >>> print token_json['oauth_token_consumer'] | 39 | >>> print(token_json['oauth_token_consumer']) |
27 | 40 | consumer | 40 | consumer |
28 | diff --git a/src/launchpadlib/docs/hosted-files.rst b/src/launchpadlib/docs/hosted-files.rst | |||
29 | index 98d8125..1ba1beb 100644 | |||
30 | --- a/src/launchpadlib/docs/hosted-files.rst | |||
31 | +++ b/src/launchpadlib/docs/hosted-files.rst | |||
32 | @@ -16,11 +16,11 @@ you try to upload a non-image for a field that expects an image. | |||
33 | 16 | 'image/png' | 16 | 'image/png' |
34 | 17 | >>> file_handle.filename | 17 | >>> file_handle.filename |
35 | 18 | 'nonimage.txt' | 18 | 'nonimage.txt' |
37 | 19 | >>> file_handle.write("Not an image.") | 19 | >>> file_handle.write(b"Not an image.") |
38 | 20 | >>> try: | 20 | >>> try: |
39 | 21 | ... file_handle.close() | 21 | ... file_handle.close() |
42 | 22 | ... except HTTPError, e: | 22 | ... except HTTPError as e: |
43 | 23 | ... print e.content | 23 | ... print(e.content.decode()) |
44 | 24 | <BLANKLINE> | 24 | <BLANKLINE> |
45 | 25 | The file uploaded was not recognized as an image; please | 25 | The file uploaded was not recognized as an image; please |
46 | 26 | check it and retry. | 26 | check it and retry. |
47 | @@ -31,7 +31,8 @@ Of course, uploading an image works fine. | |||
48 | 31 | >>> def load_image(filename): | 31 | >>> def load_image(filename): |
49 | 32 | ... image_file = os.path.join( | 32 | ... image_file = os.path.join( |
50 | 33 | ... os.path.dirname(__file__), 'files', filename) | 33 | ... os.path.dirname(__file__), 'files', filename) |
52 | 34 | ... return open(image_file).read() | 34 | ... with open(image_file, "rb") as f: |
53 | 35 | ... return f.read() | ||
54 | 35 | >>> image = load_image("mugshot.png") | 36 | >>> image = load_image("mugshot.png") |
55 | 36 | >>> len(image) | 37 | >>> len(image) |
56 | 37 | 2260 | 38 | 2260 |
57 | @@ -51,11 +52,11 @@ written to a particular file. | |||
58 | 51 | 'image/png' | 52 | 'image/png' |
59 | 52 | >>> file_handle.filename | 53 | >>> file_handle.filename |
60 | 53 | 'nonimage.txt' | 54 | 'nonimage.txt' |
62 | 54 | >>> file_handle.write("Not an image.") | 55 | >>> file_handle.write(b"Not an image.") |
63 | 55 | >>> file_handle.close() | 56 | >>> file_handle.close() |
64 | 56 | Traceback (most recent call last): | 57 | Traceback (most recent call last): |
65 | 57 | ... | 58 | ... |
67 | 58 | BadRequest: HTTP Error 400: Bad Request | 59 | lazr.restfulclient.errors.BadRequest: HTTP Error 400: Bad Request |
68 | 59 | ... | 60 | ... |
69 | 60 | 61 | ||
70 | 61 | == Caching == | 62 | == Caching == |
71 | @@ -70,7 +71,7 @@ mechanism. | |||
72 | 70 | >>> import httplib2 | 71 | >>> import httplib2 |
73 | 71 | >>> httplib2.debuglevel = 1 | 72 | >>> httplib2.debuglevel = 1 |
74 | 72 | >>> launchpad = salgado_with_full_permissions.login() | 73 | >>> launchpad = salgado_with_full_permissions.login() |
76 | 73 | connect: ... | 74 | send: ... |
77 | 74 | >>> mugshot = launchpad.me.mugshot | 75 | >>> mugshot = launchpad.me.mugshot |
78 | 75 | send: ... | 76 | send: ... |
79 | 76 | 77 | ||
80 | @@ -90,14 +91,13 @@ already have a fresh copy. | |||
81 | 90 | >>> len(mugshot.open().read()) | 91 | >>> len(mugshot.open().read()) |
82 | 91 | send: ... | 92 | send: ... |
83 | 92 | reply: 'HTTP/1.1 303 See Other... | 93 | reply: 'HTTP/1.1 303 See Other... |
86 | 93 | header: Location: http://.../image.png | 94 | header: Location... |
85 | 94 | ... | ||
87 | 95 | 2260 | 95 | 2260 |
88 | 96 | 96 | ||
89 | 97 | Finally, some cleanup code that deletes the mugshot. | 97 | Finally, some cleanup code that deletes the mugshot. |
90 | 98 | 98 | ||
91 | 99 | >>> mugshot.delete() | 99 | >>> mugshot.delete() |
93 | 100 | send: 'DELETE... | 100 | send: b'DELETE... |
94 | 101 | reply: 'HTTP/1.1 200... | 101 | reply: 'HTTP/1.1 200... |
95 | 102 | 102 | ||
96 | 103 | >>> httplib2.debuglevel = 0 | 103 | >>> httplib2.debuglevel = 0 |
97 | diff --git a/src/launchpadlib/docs/introduction.rst b/src/launchpadlib/docs/introduction.rst | |||
98 | index 722ebac..0bb321e 100644 | |||
99 | --- a/src/launchpadlib/docs/introduction.rst | |||
100 | +++ b/src/launchpadlib/docs/introduction.rst | |||
101 | @@ -75,9 +75,9 @@ behalf. | |||
102 | 75 | >>> from launchpadlib.testing.helpers import ( | 75 | >>> from launchpadlib.testing.helpers import ( |
103 | 76 | ... TestableLaunchpad as Launchpad) | 76 | ... TestableLaunchpad as Launchpad) |
104 | 77 | >>> launchpad = Launchpad(credentials=credentials) | 77 | >>> launchpad = Launchpad(credentials=credentials) |
106 | 78 | >>> sorted(launchpad.people) | 78 | >>> list(launchpad.people) |
107 | 79 | [...] | 79 | [...] |
109 | 80 | >>> sorted(launchpad.bugs) | 80 | >>> list(launchpad.bugs) |
110 | 81 | [...] | 81 | [...] |
111 | 82 | 82 | ||
112 | 83 | If available, the Gnome keyring or KDE wallet will be used to store access | 83 | If available, the Gnome keyring or KDE wallet will be used to store access |
113 | @@ -102,9 +102,9 @@ appropriate instance variables and use the save() method. | |||
114 | 102 | And the credentials are perfectly valid for accessing Launchpad. | 102 | And the credentials are perfectly valid for accessing Launchpad. |
115 | 103 | 103 | ||
116 | 104 | >>> launchpad = Launchpad(credentials=credentials) | 104 | >>> launchpad = Launchpad(credentials=credentials) |
118 | 105 | >>> sorted(launchpad.people) | 105 | >>> list(launchpad.people) |
119 | 106 | [...] | 106 | [...] |
121 | 107 | >>> sorted(launchpad.bugs) | 107 | >>> list(launchpad.bugs) |
122 | 108 | [...] | 108 | [...] |
123 | 109 | 109 | ||
124 | 110 | The credentials can also be retrieved from the file, so that the OAuth request | 110 | The credentials can also be retrieved from the file, so that the OAuth request |
125 | @@ -126,22 +126,22 @@ dance can be avoided. | |||
126 | 126 | These credentials too, are perfectly usable to access Launchpad. | 126 | These credentials too, are perfectly usable to access Launchpad. |
127 | 127 | 127 | ||
128 | 128 | >>> launchpad = Launchpad(credentials=credentials) | 128 | >>> launchpad = Launchpad(credentials=credentials) |
130 | 129 | >>> sorted(launchpad.people) | 129 | >>> list(launchpad.people) |
131 | 130 | [...] | 130 | [...] |
133 | 131 | >>> sorted(launchpad.bugs) | 131 | >>> list(launchpad.bugs) |
134 | 132 | [...] | 132 | [...] |
135 | 133 | 133 | ||
136 | 134 | The security of the stored credentials is left up to the file-like object. | 134 | The security of the stored credentials is left up to the file-like object. |
137 | 135 | Here, the application decides to use a dubious encryption algorithm to hide | 135 | Here, the application decides to use a dubious encryption algorithm to hide |
138 | 136 | Salgado's credentials. | 136 | Salgado's credentials. |
139 | 137 | 137 | ||
141 | 138 | >>> from StringIO import StringIO | 138 | >>> import io |
142 | 139 | >>> from codecs import EncodedFile | 139 | >>> from codecs import EncodedFile |
144 | 140 | >>> encrypted_file = StringIO() | 140 | >>> encrypted_file = io.BytesIO() |
145 | 141 | >>> stream = EncodedFile(encrypted_file, 'rot_13', 'ascii') | 141 | >>> stream = EncodedFile(encrypted_file, 'rot_13', 'ascii') |
146 | 142 | >>> credentials.save(stream) | 142 | >>> credentials.save(stream) |
149 | 143 | >>> stream.seek(0, 0) | 143 | >>> _ = stream.seek(0, 0) |
150 | 144 | >>> print ''.join(sorted(encrypted_file)) | 144 | >>> print(''.join(sorted([line.decode() for line in encrypted_file]))) |
151 | 145 | [1] | 145 | [1] |
152 | 146 | <BLANKLINE> | 146 | <BLANKLINE> |
153 | 147 | <BLANKLINE> | 147 | <BLANKLINE> |
154 | @@ -150,7 +150,7 @@ Salgado's credentials. | |||
155 | 150 | pbafhzre_frperg = | 150 | pbafhzre_frperg = |
156 | 151 | pbafhzre_xrl = ynhapucnq-yvoenel | 151 | pbafhzre_xrl = ynhapucnq-yvoenel |
157 | 152 | 152 | ||
159 | 153 | >>> stream.seek(0) | 153 | >>> _ = stream.seek(0) |
160 | 154 | >>> credentials = Credentials() | 154 | >>> credentials = Credentials() |
161 | 155 | >>> credentials.load(stream) | 155 | >>> credentials.load(stream) |
162 | 156 | >>> credentials.consumer.key | 156 | >>> credentials.consumer.key |
163 | @@ -179,7 +179,7 @@ of the Launchpad dataset. | |||
164 | 179 | >>> launchpad = Launchpad(credentials=credentials) | 179 | >>> launchpad = Launchpad(credentials=credentials) |
165 | 180 | 180 | ||
166 | 181 | >>> salgado = launchpad.people['salgado'] | 181 | >>> salgado = launchpad.people['salgado'] |
168 | 182 | >>> print salgado.display_name | 182 | >>> print(salgado.display_name) |
169 | 183 | Guilherme Salgado | 183 | Guilherme Salgado |
170 | 184 | 184 | ||
171 | 185 | An anonymous client can't modify the dataset, or read any data that's | 185 | An anonymous client can't modify the dataset, or read any data that's |
172 | @@ -188,14 +188,14 @@ permission-controlled or scoped to a particular user. | |||
173 | 188 | >>> launchpad.me | 188 | >>> launchpad.me |
174 | 189 | Traceback (most recent call last): | 189 | Traceback (most recent call last): |
175 | 190 | ... | 190 | ... |
177 | 191 | Unauthorized: HTTP Error 401: Unauthorized | 191 | lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized |
178 | 192 | ... | 192 | ... |
179 | 193 | 193 | ||
180 | 194 | >>> salgado.display_name = "This won't work." | 194 | >>> salgado.display_name = "This won't work." |
181 | 195 | >>> salgado.lp_save() | 195 | >>> salgado.lp_save() |
182 | 196 | Traceback (most recent call last): | 196 | Traceback (most recent call last): |
183 | 197 | ... | 197 | ... |
185 | 198 | Unauthorized: HTTP Error 401: Unauthorized | 198 | lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized |
186 | 199 | ... | 199 | ... |
187 | 200 | 200 | ||
188 | 201 | Convenience | 201 | Convenience |
189 | @@ -207,13 +207,13 @@ to provide is the consumer name. | |||
190 | 207 | 207 | ||
191 | 208 | >>> launchpad = Launchpad.login_anonymously( | 208 | >>> launchpad = Launchpad.login_anonymously( |
192 | 209 | ... 'launchpad-library', service_root="test_dev") | 209 | ... 'launchpad-library', service_root="test_dev") |
194 | 210 | >>> sorted(launchpad.people) | 210 | >>> list(launchpad.people) |
195 | 211 | [...] | 211 | [...] |
196 | 212 | 212 | ||
197 | 213 | >>> launchpad.me | 213 | >>> launchpad.me |
198 | 214 | Traceback (most recent call last): | 214 | Traceback (most recent call last): |
199 | 215 | ... | 215 | ... |
201 | 216 | Unauthorized: HTTP Error 401: Unauthorized | 216 | lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized |
202 | 217 | ... | 217 | ... |
203 | 218 | 218 | ||
204 | 219 | Otherwise, the application should obtain authorization from the user | 219 | Otherwise, the application should obtain authorization from the user |
205 | @@ -244,7 +244,7 @@ attribute of the Credentials object. | |||
206 | 244 | True | 244 | True |
207 | 245 | >>> credentials._request_token.secret is not None | 245 | >>> credentials._request_token.secret is not None |
208 | 246 | True | 246 | True |
210 | 247 | >>> print credentials._request_token.context | 247 | >>> print(credentials._request_token.context) |
211 | 248 | firefox | 248 | firefox |
212 | 249 | 249 | ||
213 | 250 | Now the user must authorize that token, and this is the part we can't | 250 | Now the user must authorize that token, and this is the part we can't |
214 | @@ -282,7 +282,7 @@ fields set as if you had asked for the default URI token format. | |||
215 | 282 | True | 282 | True |
216 | 283 | >>> credentials._request_token.secret is not None | 283 | >>> credentials._request_token.secret is not None |
217 | 284 | True | 284 | True |
219 | 285 | >>> print credentials._request_token.context | 285 | >>> print(credentials._request_token.context) |
220 | 286 | firefox | 286 | firefox |
221 | 287 | 287 | ||
222 | 288 | 288 | ||
223 | @@ -292,32 +292,33 @@ Credentials file errors | |||
224 | 292 | If the credentials file is empty, loading it raises an exception. | 292 | If the credentials file is empty, loading it raises an exception. |
225 | 293 | 293 | ||
226 | 294 | >>> credentials = Credentials() | 294 | >>> credentials = Credentials() |
228 | 295 | >>> credentials.load(StringIO()) | 295 | >>> credentials.load(io.StringIO()) |
229 | 296 | Traceback (most recent call last): | 296 | Traceback (most recent call last): |
230 | 297 | ... | 297 | ... |
232 | 298 | CredentialsFileError: No configuration for version 1 | 298 | lazr.restfulclient.errors.CredentialsFileError: No configuration for |
233 | 299 | version 1 | ||
234 | 299 | 300 | ||
235 | 300 | It is an error to save a credentials file when no consumer or access token is | 301 | It is an error to save a credentials file when no consumer or access token is |
236 | 301 | available. | 302 | available. |
237 | 302 | 303 | ||
238 | 303 | >>> credentials.consumer = None | 304 | >>> credentials.consumer = None |
240 | 304 | >>> credentials.save(StringIO()) | 305 | >>> credentials.save(io.StringIO()) |
241 | 305 | Traceback (most recent call last): | 306 | Traceback (most recent call last): |
242 | 306 | ... | 307 | ... |
244 | 307 | CredentialsFileError: No consumer | 308 | lazr.restfulclient.errors.CredentialsFileError: No consumer |
245 | 308 | 309 | ||
246 | 309 | >>> credentials.consumer = consumer | 310 | >>> credentials.consumer = consumer |
247 | 310 | >>> credentials.access_token = None | 311 | >>> credentials.access_token = None |
249 | 311 | >>> credentials.save(StringIO()) | 312 | >>> credentials.save(io.StringIO()) |
250 | 312 | Traceback (most recent call last): | 313 | Traceback (most recent call last): |
251 | 313 | ... | 314 | ... |
253 | 314 | CredentialsFileError: No access token | 315 | lazr.restfulclient.errors.CredentialsFileError: No access token |
254 | 315 | 316 | ||
255 | 316 | The credentials file is not intended to be edited, but because it's human | 317 | The credentials file is not intended to be edited, but because it's human |
256 | 317 | readable, that's of course possible. If the credentials file gets corrupted, | 318 | readable, that's of course possible. If the credentials file gets corrupted, |
257 | 318 | an error is raised. | 319 | an error is raised. |
258 | 319 | 320 | ||
260 | 320 | >>> credentials_file = StringIO("""\ | 321 | >>> credentials_file = io.StringIO("""\ |
261 | 321 | ... [1] | 322 | ... [1] |
262 | 322 | ... #consumer_key: aardvark | 323 | ... #consumer_key: aardvark |
263 | 323 | ... consumer_secret: badger | 324 | ... consumer_secret: badger |
264 | @@ -327,9 +328,9 @@ an error is raised. | |||
265 | 327 | >>> credentials.load(credentials_file) | 328 | >>> credentials.load(credentials_file) |
266 | 328 | Traceback (most recent call last): | 329 | Traceback (most recent call last): |
267 | 329 | ... | 330 | ... |
269 | 330 | NoOptionError: No option 'consumer_key' in section: '1' | 331 | configparser.NoOptionError: No option 'consumer_key' in section: '1' |
270 | 331 | 332 | ||
272 | 332 | >>> credentials_file = StringIO("""\ | 333 | >>> credentials_file = io.StringIO("""\ |
273 | 333 | ... [1] | 334 | ... [1] |
274 | 334 | ... consumer_key: aardvark | 335 | ... consumer_key: aardvark |
275 | 335 | ... #consumer_secret: badger | 336 | ... #consumer_secret: badger |
276 | @@ -339,9 +340,9 @@ an error is raised. | |||
277 | 339 | >>> credentials.load(credentials_file) | 340 | >>> credentials.load(credentials_file) |
278 | 340 | Traceback (most recent call last): | 341 | Traceback (most recent call last): |
279 | 341 | ... | 342 | ... |
281 | 342 | NoOptionError: No option 'consumer_secret' in section: '1' | 343 | configparser.NoOptionError: No option 'consumer_secret' in section: '1' |
282 | 343 | 344 | ||
284 | 344 | >>> credentials_file = StringIO("""\ | 345 | >>> credentials_file = io.StringIO("""\ |
285 | 345 | ... [1] | 346 | ... [1] |
286 | 346 | ... consumer_key: aardvark | 347 | ... consumer_key: aardvark |
287 | 347 | ... consumer_secret: badger | 348 | ... consumer_secret: badger |
288 | @@ -351,9 +352,9 @@ an error is raised. | |||
289 | 351 | >>> credentials.load(credentials_file) | 352 | >>> credentials.load(credentials_file) |
290 | 352 | Traceback (most recent call last): | 353 | Traceback (most recent call last): |
291 | 353 | ... | 354 | ... |
293 | 354 | NoOptionError: No option 'access_token' in section: '1' | 355 | configparser.NoOptionError: No option 'access_token' in section: '1' |
294 | 355 | 356 | ||
296 | 356 | >>> credentials_file = StringIO("""\ | 357 | >>> credentials_file = io.StringIO("""\ |
297 | 357 | ... [1] | 358 | ... [1] |
298 | 358 | ... consumer_key: aardvark | 359 | ... consumer_key: aardvark |
299 | 359 | ... consumer_secret: badger | 360 | ... consumer_secret: badger |
300 | @@ -363,7 +364,7 @@ an error is raised. | |||
301 | 363 | >>> credentials.load(credentials_file) | 364 | >>> credentials.load(credentials_file) |
302 | 364 | Traceback (most recent call last): | 365 | Traceback (most recent call last): |
303 | 365 | ... | 366 | ... |
305 | 366 | NoOptionError: No option 'access_secret' in section: '1' | 367 | configparser.NoOptionError: No option 'access_secret' in section: '1' |
306 | 367 | 368 | ||
307 | 368 | 369 | ||
308 | 369 | Bad credentials | 370 | Bad credentials |
309 | @@ -378,7 +379,7 @@ The application is not allowed to access Launchpad with a bad access token. | |||
310 | 378 | >>> launchpad = Launchpad(credentials=credentials) | 379 | >>> launchpad = Launchpad(credentials=credentials) |
311 | 379 | Traceback (most recent call last): | 380 | Traceback (most recent call last): |
312 | 380 | ... | 381 | ... |
314 | 381 | Unauthorized: HTTP Error 401: Unauthorized | 382 | lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized |
315 | 382 | ... | 383 | ... |
316 | 383 | 384 | ||
317 | 384 | The application is not allowed to access Launchpad with a consumer | 385 | The application is not allowed to access Launchpad with a consumer |
318 | @@ -391,7 +392,7 @@ name that doesn't match the credentials. | |||
319 | 391 | >>> launchpad = Launchpad(credentials=credentials) | 392 | >>> launchpad = Launchpad(credentials=credentials) |
320 | 392 | Traceback (most recent call last): | 393 | Traceback (most recent call last): |
321 | 393 | ... | 394 | ... |
323 | 394 | Unauthorized: HTTP Error 401: Unauthorized | 395 | lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized |
324 | 395 | ... | 396 | ... |
325 | 396 | 397 | ||
326 | 397 | The application is not allowed to access Launchpad with a bad access secret. | 398 | The application is not allowed to access Launchpad with a bad access secret. |
327 | @@ -403,7 +404,7 @@ The application is not allowed to access Launchpad with a bad access secret. | |||
328 | 403 | >>> launchpad = Launchpad(credentials=credentials) | 404 | >>> launchpad = Launchpad(credentials=credentials) |
329 | 404 | Traceback (most recent call last): | 405 | Traceback (most recent call last): |
330 | 405 | ... | 406 | ... |
332 | 406 | Unauthorized: HTTP Error 401: Unauthorized | 407 | lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized |
333 | 407 | ... | 408 | ... |
334 | 408 | 409 | ||
335 | 409 | Clean up | 410 | Clean up |
336 | diff --git a/src/launchpadlib/docs/people.rst b/src/launchpadlib/docs/people.rst | |||
337 | index d1dc629..83a98c8 100644 | |||
338 | --- a/src/launchpadlib/docs/people.rst | |||
339 | +++ b/src/launchpadlib/docs/people.rst | |||
340 | @@ -21,29 +21,29 @@ The list of people is available from the service root. | |||
341 | 21 | 21 | ||
342 | 22 | The list of people is not fetched until you actually use data. | 22 | The list of people is not fetched until you actually use data. |
343 | 23 | 23 | ||
345 | 24 | >>> print people._wadl_resource.representation | 24 | >>> print(people._wadl_resource.representation) |
346 | 25 | None | 25 | None |
347 | 26 | 26 | ||
348 | 27 | >>> len(people) | 27 | >>> len(people) |
349 | 28 | 4 | 28 | 4 |
350 | 29 | 29 | ||
352 | 30 | >>> print people._wadl_resource.representation | 30 | >>> print(people._wadl_resource.representation) |
353 | 31 | {...} | 31 | {...} |
354 | 32 | 32 | ||
355 | 33 | The 'me' attribute is also available from the service root. It's a | 33 | The 'me' attribute is also available from the service root. It's a |
356 | 34 | quick way to get a reference to your own user account. | 34 | quick way to get a reference to your own user account. |
357 | 35 | 35 | ||
358 | 36 | >>> me = launchpad.me | 36 | >>> me = launchpad.me |
361 | 37 | >>> me.name | 37 | >>> print(me.name) |
362 | 38 | u'salgado' | 38 | salgado |
363 | 39 | 39 | ||
364 | 40 | You can find a person by name. | 40 | You can find a person by name. |
365 | 41 | 41 | ||
366 | 42 | >>> salgado = launchpad.people['salgado'] | 42 | >>> salgado = launchpad.people['salgado'] |
371 | 43 | >>> salgado.name | 43 | >>> print(salgado.name) |
372 | 44 | u'salgado' | 44 | salgado |
373 | 45 | >>> salgado.display_name | 45 | >>> print(salgado.display_name) |
374 | 46 | u'Guilherme Salgado' | 46 | Guilherme Salgado |
375 | 47 | >>> salgado.is_team | 47 | >>> salgado.is_team |
376 | 48 | False | 48 | False |
377 | 49 | 49 | ||
378 | @@ -67,8 +67,8 @@ You can find a person by email. | |||
379 | 67 | 67 | ||
380 | 68 | >>> email = salgado.preferred_email_address.email | 68 | >>> email = salgado.preferred_email_address.email |
381 | 69 | >>> salgado = launchpad.people.getByEmail(email=email) | 69 | >>> salgado = launchpad.people.getByEmail(email=email) |
384 | 70 | >>> salgado.name | 70 | >>> print(salgado.name) |
385 | 71 | u'salgado' | 71 | salgado |
386 | 72 | 72 | ||
387 | 73 | Besides a name and a display name, a person has many other attributes that you | 73 | Besides a name and a display name, a person has many other attributes that you |
388 | 74 | can read. | 74 | can read. |
389 | @@ -79,7 +79,7 @@ can read. | |||
390 | 79 | 79 | ||
391 | 80 | >>> salgado.karma | 80 | >>> salgado.karma |
392 | 81 | 0 | 81 | 0 |
394 | 82 | >>> print salgado.homepage_content | 82 | >>> print(salgado.homepage_content) |
395 | 83 | None | 83 | None |
396 | 84 | >>> #salgado.mugshot | 84 | >>> #salgado.mugshot |
397 | 85 | >>> #salgado.languages | 85 | >>> #salgado.languages |
398 | @@ -87,7 +87,7 @@ can read. | |||
399 | 87 | False | 87 | False |
400 | 88 | >>> salgado.date_created | 88 | >>> salgado.date_created |
401 | 89 | datetime.datetime(2005, 6, 6, 8, 59, 51, 596025, ...) | 89 | datetime.datetime(2005, 6, 6, 8, 59, 51, 596025, ...) |
403 | 90 | >>> print salgado.time_zone | 90 | >>> print(salgado.time_zone) |
404 | 91 | UTC | 91 | UTC |
405 | 92 | >>> salgado.is_valid | 92 | >>> salgado.is_valid |
406 | 93 | True | 93 | True |
407 | @@ -100,10 +100,10 @@ can read. | |||
408 | 100 | >>> #salgado.teams_indirectly_participated_in | 100 | >>> #salgado.teams_indirectly_participated_in |
409 | 101 | >>> #salgado.confirmed_email_addresses | 101 | >>> #salgado.confirmed_email_addresses |
410 | 102 | >>> #salgado.preferred_email_address | 102 | >>> #salgado.preferred_email_address |
415 | 103 | >>> salgado.mailing_list_auto_subscribe_policy | 103 | >>> print(salgado.mailing_list_auto_subscribe_policy) |
416 | 104 | u'Ask me when I join a team' | 104 | Ask me when I join a team |
417 | 105 | >>> salgado.visibility | 105 | >>> print(salgado.visibility) |
418 | 106 | u'Public' | 106 | Public |
419 | 107 | 107 | ||
420 | 108 | 108 | ||
421 | 109 | Teams | 109 | Teams |
422 | @@ -112,16 +112,16 @@ Teams | |||
423 | 112 | You also access teams using the same interface. | 112 | You also access teams using the same interface. |
424 | 113 | 113 | ||
425 | 114 | >>> team = launchpad.people['ubuntu-team'] | 114 | >>> team = launchpad.people['ubuntu-team'] |
430 | 115 | >>> team.name | 115 | >>> print(team.name) |
431 | 116 | u'ubuntu-team' | 116 | ubuntu-team |
432 | 117 | >>> team.display_name | 117 | >>> print(team.display_name) |
433 | 118 | u'Ubuntu Team' | 118 | Ubuntu Team |
434 | 119 | >>> team.is_team | 119 | >>> team.is_team |
435 | 120 | True | 120 | True |
436 | 121 | 121 | ||
437 | 122 | Regular people have team attributes, but they're not used. | 122 | Regular people have team attributes, but they're not used. |
438 | 123 | 123 | ||
440 | 124 | >>> print salgado.team_owner | 124 | >>> print(salgado.team_owner) |
441 | 125 | None | 125 | None |
442 | 126 | 126 | ||
443 | 127 | You can find out how a person has membership in a team. | 127 | You can find out how a person has membership in a team. |
444 | @@ -143,27 +143,27 @@ this requires only the new team's name, owner and display name. | |||
445 | 143 | 143 | ||
446 | 144 | >>> bassists = launchpad.people.newTeam( | 144 | >>> bassists = launchpad.people.newTeam( |
447 | 145 | ... name='bassists', display_name='Awesome Rock Bass Players') | 145 | ... name='bassists', display_name='Awesome Rock Bass Players') |
452 | 146 | >>> bassists.name | 146 | >>> print(bassists.name) |
453 | 147 | u'bassists' | 147 | bassists |
454 | 148 | >>> bassists.display_name | 148 | >>> print(bassists.display_name) |
455 | 149 | u'Awesome Rock Bass Players' | 149 | Awesome Rock Bass Players |
456 | 150 | >>> bassists.is_team | 150 | >>> bassists.is_team |
457 | 151 | True | 151 | True |
458 | 152 | 152 | ||
459 | 153 | And of course, that team is now accessible directly. | 153 | And of course, that team is now accessible directly. |
460 | 154 | 154 | ||
461 | 155 | >>> bassists = launchpad.people['bassists'] | 155 | >>> bassists = launchpad.people['bassists'] |
466 | 156 | >>> bassists.name | 156 | >>> print(bassists.name) |
467 | 157 | u'bassists' | 157 | bassists |
468 | 158 | >>> bassists.display_name | 158 | >>> print(bassists.display_name) |
469 | 159 | u'Awesome Rock Bass Players' | 159 | Awesome Rock Bass Players |
470 | 160 | 160 | ||
471 | 161 | You cannot create the same team twice. | 161 | You cannot create the same team twice. |
472 | 162 | 162 | ||
473 | 163 | >>> launchpad.people.newTeam(name='bassists', display_name='Bass Gods') | 163 | >>> launchpad.people.newTeam(name='bassists', display_name='Bass Gods') |
474 | 164 | Traceback (most recent call last): | 164 | Traceback (most recent call last): |
475 | 165 | ... | 165 | ... |
477 | 166 | BadRequest: HTTP Error 400: Bad Request | 166 | lazr.restfulclient.errors.BadRequest: HTTP Error 400: Bad Request |
478 | 167 | ... | 167 | ... |
479 | 168 | 168 | ||
480 | 169 | Actually, the exception contains other useful information. | 169 | Actually, the exception contains other useful information. |
481 | @@ -172,25 +172,25 @@ Actually, the exception contains other useful information. | |||
482 | 172 | >>> try: | 172 | >>> try: |
483 | 173 | ... launchpad.people.newTeam( | 173 | ... launchpad.people.newTeam( |
484 | 174 | ... name='bassists', display_name='Bass Gods') | 174 | ... name='bassists', display_name='Bass Gods') |
487 | 175 | ... except HTTPError, error: | 175 | ... except HTTPError as e: |
488 | 176 | ... pass | 176 | ... error = e |
489 | 177 | >>> error.response['status'] | 177 | >>> error.response['status'] |
490 | 178 | '400' | 178 | '400' |
493 | 179 | >>> error.content | 179 | >>> print(error.content.decode()) |
494 | 180 | 'name: bassists is already in use by another person or team.' | 180 | name: bassists is already in use by another person or team. |
495 | 181 | 181 | ||
496 | 182 | Besides a name and a display name, a team has many other attributes that you | 182 | Besides a name and a display name, a team has many other attributes that you |
497 | 183 | can read. | 183 | can read. |
498 | 184 | 184 | ||
499 | 185 | >>> bassists.karma | 185 | >>> bassists.karma |
500 | 186 | 0 | 186 | 0 |
502 | 187 | >>> print bassists.homepage_content | 187 | >>> print(bassists.homepage_content) |
503 | 188 | None | 188 | None |
504 | 189 | >>> bassists.hide_email_addresses | 189 | >>> bassists.hide_email_addresses |
505 | 190 | False | 190 | False |
506 | 191 | >>> bassists.date_created | 191 | >>> bassists.date_created |
507 | 192 | datetime.datetime(...) | 192 | datetime.datetime(...) |
509 | 193 | >>> print bassists.time_zone | 193 | >>> print(bassists.time_zone) |
510 | 194 | UTC | 194 | UTC |
511 | 195 | >>> bassists.is_valid | 195 | >>> bassists.is_valid |
512 | 196 | True | 196 | True |
513 | @@ -209,15 +209,15 @@ can read. | |||
514 | 209 | >>> #bassists.invited_members | 209 | >>> #bassists.invited_members |
515 | 210 | >>> #bassists.member_memberships | 210 | >>> #bassists.member_memberships |
516 | 211 | >>> #bassists.proposed_members | 211 | >>> #bassists.proposed_members |
520 | 212 | >>> bassists.visibility | 212 | >>> print(bassists.visibility) |
521 | 213 | u'Public' | 213 | Public |
522 | 214 | >>> print bassists.team_description | 214 | >>> print(bassists.team_description) |
523 | 215 | None | 215 | None |
529 | 216 | >>> bassists.subscription_policy | 216 | >>> print(bassists.subscription_policy) |
530 | 217 | u'Moderated Team' | 217 | Moderated Team |
531 | 218 | >>> bassists.renewal_policy | 218 | >>> print(bassists.renewal_policy) |
532 | 219 | u'invite them to apply for renewal' | 219 | invite them to apply for renewal |
533 | 220 | >>> print bassists.default_membership_period | 220 | >>> print(bassists.default_membership_period) |
534 | 221 | None | 221 | None |
536 | 222 | >>> print bassists.default_renewal_period | 222 | >>> print(bassists.default_renewal_period) |
537 | 223 | None | 223 | None |
538 | diff --git a/src/launchpadlib/docs/toplevel.rst b/src/launchpadlib/docs/toplevel.rst | |||
539 | index 76d2a56..67c480a 100644 | |||
540 | --- a/src/launchpadlib/docs/toplevel.rst | |||
541 | +++ b/src/launchpadlib/docs/toplevel.rst | |||
542 | @@ -10,14 +10,14 @@ Launchpad-wide objects like projects and people. | |||
543 | 10 | 10 | ||
544 | 11 | >>> from launchpadlib.testing.helpers import salgado_with_full_permissions | 11 | >>> from launchpadlib.testing.helpers import salgado_with_full_permissions |
545 | 12 | >>> launchpad = salgado_with_full_permissions.login() | 12 | >>> launchpad = salgado_with_full_permissions.login() |
547 | 13 | connect: ... | 13 | send: ... |
548 | 14 | ... | 14 | ... |
549 | 15 | 15 | ||
550 | 16 | It's possible to do key-based lookups on the top-level | 16 | It's possible to do key-based lookups on the top-level |
551 | 17 | collections. The bug collection does lookups by bug ID. | 17 | collections. The bug collection does lookups by bug ID. |
552 | 18 | 18 | ||
553 | 19 | >>> bug = launchpad.bugs[1] | 19 | >>> bug = launchpad.bugs[1] |
555 | 20 | send: 'GET /.../bugs/1 ...' | 20 | send: b'GET /.../bugs/1 ...' |
556 | 21 | ... | 21 | ... |
557 | 22 | 22 | ||
558 | 23 | To avoid triggering an HTTP request when simply looking up an object, | 23 | To avoid triggering an HTTP request when simply looking up an object, |
559 | @@ -28,8 +28,8 @@ you can use a different syntax: | |||
560 | 28 | The HTTP request will happen when you need information that can only | 28 | The HTTP request will happen when you need information that can only |
561 | 29 | be obtained from the web service. | 29 | be obtained from the web service. |
562 | 30 | 30 | ||
565 | 31 | >>> print bug.id | 31 | >>> print(bug.id) |
566 | 32 | send: 'GET /.../bugs/1 ...' | 32 | send: b'GET /.../bugs/1 ...' |
567 | 33 | ... | 33 | ... |
568 | 34 | 1 | 34 | 1 |
569 | 35 | 35 | ||
570 | @@ -37,24 +37,24 @@ Let's look at some more collections. The project collection does | |||
571 | 37 | lookups by project name. | 37 | lookups by project name. |
572 | 38 | 38 | ||
573 | 39 | >>> project = launchpad.projects('firefox') | 39 | >>> project = launchpad.projects('firefox') |
576 | 40 | >>> print project.name | 40 | >>> print(project.name) |
577 | 41 | send: 'GET /.../firefox ...' | 41 | send: b'GET /.../firefox ...' |
578 | 42 | ... | 42 | ... |
579 | 43 | firefox | 43 | firefox |
580 | 44 | 44 | ||
581 | 45 | The project group collection does lookups by project group name. | 45 | The project group collection does lookups by project group name. |
582 | 46 | 46 | ||
583 | 47 | >>> group = launchpad.project_groups('gnome') | 47 | >>> group = launchpad.project_groups('gnome') |
586 | 48 | >>> print group.name | 48 | >>> print(group.name) |
587 | 49 | send: 'GET /.../gnome ...' | 49 | send: b'GET /.../gnome ...' |
588 | 50 | ... | 50 | ... |
589 | 51 | gnome | 51 | gnome |
590 | 52 | 52 | ||
591 | 53 | The distribution collection does lookups by distribution name. | 53 | The distribution collection does lookups by distribution name. |
592 | 54 | 54 | ||
593 | 55 | >>> distribution = launchpad.distributions('ubuntu') | 55 | >>> distribution = launchpad.distributions('ubuntu') |
596 | 56 | >>> print distribution.name | 56 | >>> print(distribution.name) |
597 | 57 | send: 'GET /.../ubuntu ...' | 57 | send: b'GET /.../ubuntu ...' |
598 | 58 | ... | 58 | ... |
599 | 59 | ubuntu | 59 | ubuntu |
600 | 60 | 60 | ||
601 | @@ -62,26 +62,26 @@ The person collection does lookups by a person's Launchpad | |||
602 | 62 | name. | 62 | name. |
603 | 63 | 63 | ||
604 | 64 | >>> person = launchpad.people('salgado') | 64 | >>> person = launchpad.people('salgado') |
607 | 65 | >>> print person.name | 65 | >>> print(person.name) |
608 | 66 | send: 'GET /.../~salgado ...' | 66 | send: b'GET /.../~salgado ...' |
609 | 67 | ... | 67 | ... |
610 | 68 | salgado | 68 | salgado |
611 | 69 | 69 | ||
612 | 70 | >>> team = launchpad.people('rosetta-admins') | 70 | >>> team = launchpad.people('rosetta-admins') |
615 | 71 | >>> print team.name | 71 | >>> print(team.name) |
616 | 72 | send: 'GET /1.0/~rosetta-admins ...' | 72 | send: b'GET /1.0/~rosetta-admins ...' |
617 | 73 | ... | 73 | ... |
618 | 74 | rosetta-admins | 74 | rosetta-admins |
619 | 75 | 75 | ||
620 | 76 | How does launchpadlib know that 'salgado' is a person and | 76 | How does launchpadlib know that 'salgado' is a person and |
621 | 77 | 'rosetta-admins' is a team? | 77 | 'rosetta-admins' is a team? |
622 | 78 | 78 | ||
624 | 79 | >>> print person.resource_type_link | 79 | >>> print(person.resource_type_link) |
625 | 80 | http://.../1.0/#person | 80 | http://.../1.0/#person |
626 | 81 | >>> 'default_membership_period' in person.lp_attributes | 81 | >>> 'default_membership_period' in person.lp_attributes |
627 | 82 | False | 82 | False |
628 | 83 | 83 | ||
630 | 84 | >>> print team.resource_type_link | 84 | >>> print(team.resource_type_link) |
631 | 85 | http://.../1.0/#team | 85 | http://.../1.0/#team |
632 | 86 | >>> 'default_membership_period' in team.lp_attributes | 86 | >>> 'default_membership_period' in team.lp_attributes |
633 | 87 | True | 87 | True |
634 | @@ -99,8 +99,8 @@ But accessing any attribute of an object--even trying to see what kind | |||
635 | 99 | of object 'salgado' is--will trigger the HTTP request that will | 99 | of object 'salgado' is--will trigger the HTTP request that will |
636 | 100 | determine that 'salgado' is actually a person. | 100 | determine that 'salgado' is actually a person. |
637 | 101 | 101 | ||
640 | 102 | >>> print person2.resource_type_link | 102 | >>> print(person2.resource_type_link) |
641 | 103 | send: 'GET /.../~salgado ...' | 103 | send: b'GET /.../~salgado ...' |
642 | 104 | ... | 104 | ... |
643 | 105 | http://.../1.0/#person | 105 | http://.../1.0/#person |
644 | 106 | 106 | ||
645 | @@ -118,4 +118,4 @@ to be a team. | |||
646 | 118 | 118 | ||
647 | 119 | Cleanup. | 119 | Cleanup. |
648 | 120 | 120 | ||
650 | 121 | >>> httplib2.debuglevel = None | 121 | >>> httplib2.debuglevel = 0 |
I was wondering why this package is not tested in isolation... but then again I found this line:
>>> web_root = "http:// launchpad. test:8085/"