Merge ~cjwatson/launchpadlib:py3-doctests into launchpadlib: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)
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
Jürgen Gmach (jugmac00) wrote :

I was wondering why this package is not tested in isolation... but then again I found this line:

>>> web_root = "http://launchpad.test:8085/"

review: Approve
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
diff --git a/NEWS.rst b/NEWS.rst
index ac72636..d1ab45a 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -6,6 +6,7 @@ NEWS for launchpadlib
6====================6====================
7- Generate coverage report.7- Generate coverage report.
8- Use ``pytest`` as test runner.8- Use ``pytest`` as test runner.
9- Fix doctests for Python 3.
910
101.10.16 (2022-01-21)111.10.16 (2022-01-21)
11====================12====================
diff --git a/src/launchpadlib/docs/command-line.rst b/src/launchpadlib/docs/command-line.rst
index 8fd0ef8..4f8d6e9 100644
--- a/src/launchpadlib/docs/command-line.rst
+++ b/src/launchpadlib/docs/command-line.rst
@@ -33,8 +33,8 @@ token and the available access levels.
33 oauth_token_consumer33 oauth_token_consumer
34 oauth_token_secret34 oauth_token_secret
3535
36 >>> print token_json['lp.context']36 >>> print(token_json['lp.context'])
37 context37 context
3838
39 >>> print token_json['oauth_token_consumer']39 >>> print(token_json['oauth_token_consumer'])
40 consumer40 consumer
diff --git a/src/launchpadlib/docs/hosted-files.rst b/src/launchpadlib/docs/hosted-files.rst
index 98d8125..1ba1beb 100644
--- a/src/launchpadlib/docs/hosted-files.rst
+++ b/src/launchpadlib/docs/hosted-files.rst
@@ -16,11 +16,11 @@ you try to upload a non-image for a field that expects an image.
16 'image/png'16 'image/png'
17 >>> file_handle.filename17 >>> file_handle.filename
18 'nonimage.txt'18 'nonimage.txt'
19 >>> file_handle.write("Not an image.")19 >>> file_handle.write(b"Not an image.")
20 >>> try:20 >>> try:
21 ... file_handle.close()21 ... file_handle.close()
22 ... except HTTPError, e:22 ... except HTTPError as e:
23 ... print e.content23 ... print(e.content.decode())
24 <BLANKLINE>24 <BLANKLINE>
25 The file uploaded was not recognized as an image; please25 The file uploaded was not recognized as an image; please
26 check it and retry.26 check it and retry.
@@ -31,7 +31,8 @@ Of course, uploading an image works fine.
31 >>> def load_image(filename):31 >>> def load_image(filename):
32 ... image_file = os.path.join(32 ... image_file = os.path.join(
33 ... os.path.dirname(__file__), 'files', filename)33 ... os.path.dirname(__file__), 'files', filename)
34 ... return open(image_file).read()34 ... with open(image_file, "rb") as f:
35 ... return f.read()
35 >>> image = load_image("mugshot.png")36 >>> image = load_image("mugshot.png")
36 >>> len(image)37 >>> len(image)
37 226038 2260
@@ -51,11 +52,11 @@ written to a particular file.
51 'image/png'52 'image/png'
52 >>> file_handle.filename53 >>> file_handle.filename
53 'nonimage.txt'54 'nonimage.txt'
54 >>> file_handle.write("Not an image.")55 >>> file_handle.write(b"Not an image.")
55 >>> file_handle.close()56 >>> file_handle.close()
56 Traceback (most recent call last):57 Traceback (most recent call last):
57 ...58 ...
58 BadRequest: HTTP Error 400: Bad Request59 lazr.restfulclient.errors.BadRequest: HTTP Error 400: Bad Request
59 ...60 ...
6061
61== Caching ==62== Caching ==
@@ -70,7 +71,7 @@ mechanism.
70 >>> import httplib271 >>> import httplib2
71 >>> httplib2.debuglevel = 172 >>> httplib2.debuglevel = 1
72 >>> launchpad = salgado_with_full_permissions.login()73 >>> launchpad = salgado_with_full_permissions.login()
73 connect: ...74 send: ...
74 >>> mugshot = launchpad.me.mugshot75 >>> mugshot = launchpad.me.mugshot
75 send: ...76 send: ...
7677
@@ -90,14 +91,13 @@ already have a fresh copy.
90 >>> len(mugshot.open().read())91 >>> len(mugshot.open().read())
91 send: ...92 send: ...
92 reply: 'HTTP/1.1 303 See Other...93 reply: 'HTTP/1.1 303 See Other...
93 header: Location: http://.../image.png94 header: Location...
94 ...
95 226095 2260
9696
97Finally, some cleanup code that deletes the mugshot.97Finally, some cleanup code that deletes the mugshot.
9898
99 >>> mugshot.delete()99 >>> mugshot.delete()
100 send: 'DELETE...100 send: b'DELETE...
101 reply: 'HTTP/1.1 200...101 reply: 'HTTP/1.1 200...
102102
103 >>> httplib2.debuglevel = 0103 >>> httplib2.debuglevel = 0
diff --git a/src/launchpadlib/docs/introduction.rst b/src/launchpadlib/docs/introduction.rst
index 722ebac..0bb321e 100644
--- a/src/launchpadlib/docs/introduction.rst
+++ b/src/launchpadlib/docs/introduction.rst
@@ -75,9 +75,9 @@ behalf.
75 >>> from launchpadlib.testing.helpers import (75 >>> from launchpadlib.testing.helpers import (
76 ... TestableLaunchpad as Launchpad)76 ... TestableLaunchpad as Launchpad)
77 >>> launchpad = Launchpad(credentials=credentials)77 >>> launchpad = Launchpad(credentials=credentials)
78 >>> sorted(launchpad.people)78 >>> list(launchpad.people)
79 [...]79 [...]
80 >>> sorted(launchpad.bugs)80 >>> list(launchpad.bugs)
81 [...]81 [...]
8282
83If available, the Gnome keyring or KDE wallet will be used to store access83If available, the Gnome keyring or KDE wallet will be used to store access
@@ -102,9 +102,9 @@ appropriate instance variables and use the save() method.
102And the credentials are perfectly valid for accessing Launchpad.102And the credentials are perfectly valid for accessing Launchpad.
103103
104 >>> launchpad = Launchpad(credentials=credentials)104 >>> launchpad = Launchpad(credentials=credentials)
105 >>> sorted(launchpad.people)105 >>> list(launchpad.people)
106 [...]106 [...]
107 >>> sorted(launchpad.bugs)107 >>> list(launchpad.bugs)
108 [...]108 [...]
109109
110The credentials can also be retrieved from the file, so that the OAuth request110The credentials can also be retrieved from the file, so that the OAuth request
@@ -126,22 +126,22 @@ dance can be avoided.
126These credentials too, are perfectly usable to access Launchpad.126These credentials too, are perfectly usable to access Launchpad.
127127
128 >>> launchpad = Launchpad(credentials=credentials)128 >>> launchpad = Launchpad(credentials=credentials)
129 >>> sorted(launchpad.people)129 >>> list(launchpad.people)
130 [...]130 [...]
131 >>> sorted(launchpad.bugs)131 >>> list(launchpad.bugs)
132 [...]132 [...]
133133
134The security of the stored credentials is left up to the file-like object.134The security of the stored credentials is left up to the file-like object.
135Here, the application decides to use a dubious encryption algorithm to hide135Here, the application decides to use a dubious encryption algorithm to hide
136Salgado's credentials.136Salgado's credentials.
137137
138 >>> from StringIO import StringIO138 >>> import io
139 >>> from codecs import EncodedFile139 >>> from codecs import EncodedFile
140 >>> encrypted_file = StringIO()140 >>> encrypted_file = io.BytesIO()
141 >>> stream = EncodedFile(encrypted_file, 'rot_13', 'ascii')141 >>> stream = EncodedFile(encrypted_file, 'rot_13', 'ascii')
142 >>> credentials.save(stream)142 >>> credentials.save(stream)
143 >>> stream.seek(0, 0)143 >>> _ = stream.seek(0, 0)
144 >>> print ''.join(sorted(encrypted_file))144 >>> print(''.join(sorted([line.decode() for line in encrypted_file])))
145 [1]145 [1]
146 <BLANKLINE>146 <BLANKLINE>
147 <BLANKLINE>147 <BLANKLINE>
@@ -150,7 +150,7 @@ Salgado's credentials.
150 pbafhzre_frperg =150 pbafhzre_frperg =
151 pbafhzre_xrl = ynhapucnq-yvoenel151 pbafhzre_xrl = ynhapucnq-yvoenel
152152
153 >>> stream.seek(0)153 >>> _ = stream.seek(0)
154 >>> credentials = Credentials()154 >>> credentials = Credentials()
155 >>> credentials.load(stream)155 >>> credentials.load(stream)
156 >>> credentials.consumer.key156 >>> credentials.consumer.key
@@ -179,7 +179,7 @@ of the Launchpad dataset.
179 >>> launchpad = Launchpad(credentials=credentials)179 >>> launchpad = Launchpad(credentials=credentials)
180180
181 >>> salgado = launchpad.people['salgado']181 >>> salgado = launchpad.people['salgado']
182 >>> print salgado.display_name182 >>> print(salgado.display_name)
183 Guilherme Salgado183 Guilherme Salgado
184184
185An anonymous client can't modify the dataset, or read any data that's185An anonymous client can't modify the dataset, or read any data that's
@@ -188,14 +188,14 @@ permission-controlled or scoped to a particular user.
188 >>> launchpad.me188 >>> launchpad.me
189 Traceback (most recent call last):189 Traceback (most recent call last):
190 ...190 ...
191 Unauthorized: HTTP Error 401: Unauthorized191 lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized
192 ...192 ...
193193
194 >>> salgado.display_name = "This won't work."194 >>> salgado.display_name = "This won't work."
195 >>> salgado.lp_save()195 >>> salgado.lp_save()
196 Traceback (most recent call last):196 Traceback (most recent call last):
197 ...197 ...
198 Unauthorized: HTTP Error 401: Unauthorized198 lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized
199 ...199 ...
200200
201Convenience201Convenience
@@ -207,13 +207,13 @@ to provide is the consumer name.
207207
208 >>> launchpad = Launchpad.login_anonymously(208 >>> launchpad = Launchpad.login_anonymously(
209 ... 'launchpad-library', service_root="test_dev")209 ... 'launchpad-library', service_root="test_dev")
210 >>> sorted(launchpad.people)210 >>> list(launchpad.people)
211 [...]211 [...]
212212
213 >>> launchpad.me213 >>> launchpad.me
214 Traceback (most recent call last):214 Traceback (most recent call last):
215 ...215 ...
216 Unauthorized: HTTP Error 401: Unauthorized216 lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized
217 ...217 ...
218218
219Otherwise, the application should obtain authorization from the user219Otherwise, the application should obtain authorization from the user
@@ -244,7 +244,7 @@ attribute of the Credentials object.
244 True244 True
245 >>> credentials._request_token.secret is not None245 >>> credentials._request_token.secret is not None
246 True246 True
247 >>> print credentials._request_token.context247 >>> print(credentials._request_token.context)
248 firefox248 firefox
249249
250Now the user must authorize that token, and this is the part we can't250Now the user must authorize that token, and this is the part we can't
@@ -282,7 +282,7 @@ fields set as if you had asked for the default URI token format.
282 True282 True
283 >>> credentials._request_token.secret is not None283 >>> credentials._request_token.secret is not None
284 True284 True
285 >>> print credentials._request_token.context285 >>> print(credentials._request_token.context)
286 firefox286 firefox
287287
288288
@@ -292,32 +292,33 @@ Credentials file errors
292If the credentials file is empty, loading it raises an exception.292If the credentials file is empty, loading it raises an exception.
293293
294 >>> credentials = Credentials()294 >>> credentials = Credentials()
295 >>> credentials.load(StringIO())295 >>> credentials.load(io.StringIO())
296 Traceback (most recent call last):296 Traceback (most recent call last):
297 ...297 ...
298 CredentialsFileError: No configuration for version 1298 lazr.restfulclient.errors.CredentialsFileError: No configuration for
299 version 1
299300
300It is an error to save a credentials file when no consumer or access token is301It is an error to save a credentials file when no consumer or access token is
301available.302available.
302303
303 >>> credentials.consumer = None304 >>> credentials.consumer = None
304 >>> credentials.save(StringIO())305 >>> credentials.save(io.StringIO())
305 Traceback (most recent call last):306 Traceback (most recent call last):
306 ...307 ...
307 CredentialsFileError: No consumer308 lazr.restfulclient.errors.CredentialsFileError: No consumer
308309
309 >>> credentials.consumer = consumer310 >>> credentials.consumer = consumer
310 >>> credentials.access_token = None311 >>> credentials.access_token = None
311 >>> credentials.save(StringIO())312 >>> credentials.save(io.StringIO())
312 Traceback (most recent call last):313 Traceback (most recent call last):
313 ...314 ...
314 CredentialsFileError: No access token315 lazr.restfulclient.errors.CredentialsFileError: No access token
315316
316The credentials file is not intended to be edited, but because it's human317The credentials file is not intended to be edited, but because it's human
317readable, that's of course possible. If the credentials file gets corrupted,318readable, that's of course possible. If the credentials file gets corrupted,
318an error is raised.319an error is raised.
319320
320 >>> credentials_file = StringIO("""\321 >>> credentials_file = io.StringIO("""\
321 ... [1]322 ... [1]
322 ... #consumer_key: aardvark323 ... #consumer_key: aardvark
323 ... consumer_secret: badger324 ... consumer_secret: badger
@@ -327,9 +328,9 @@ an error is raised.
327 >>> credentials.load(credentials_file)328 >>> credentials.load(credentials_file)
328 Traceback (most recent call last):329 Traceback (most recent call last):
329 ...330 ...
330 NoOptionError: No option 'consumer_key' in section: '1'331 configparser.NoOptionError: No option 'consumer_key' in section: '1'
331332
332 >>> credentials_file = StringIO("""\333 >>> credentials_file = io.StringIO("""\
333 ... [1]334 ... [1]
334 ... consumer_key: aardvark335 ... consumer_key: aardvark
335 ... #consumer_secret: badger336 ... #consumer_secret: badger
@@ -339,9 +340,9 @@ an error is raised.
339 >>> credentials.load(credentials_file)340 >>> credentials.load(credentials_file)
340 Traceback (most recent call last):341 Traceback (most recent call last):
341 ...342 ...
342 NoOptionError: No option 'consumer_secret' in section: '1'343 configparser.NoOptionError: No option 'consumer_secret' in section: '1'
343344
344 >>> credentials_file = StringIO("""\345 >>> credentials_file = io.StringIO("""\
345 ... [1]346 ... [1]
346 ... consumer_key: aardvark347 ... consumer_key: aardvark
347 ... consumer_secret: badger348 ... consumer_secret: badger
@@ -351,9 +352,9 @@ an error is raised.
351 >>> credentials.load(credentials_file)352 >>> credentials.load(credentials_file)
352 Traceback (most recent call last):353 Traceback (most recent call last):
353 ...354 ...
354 NoOptionError: No option 'access_token' in section: '1'355 configparser.NoOptionError: No option 'access_token' in section: '1'
355356
356 >>> credentials_file = StringIO("""\357 >>> credentials_file = io.StringIO("""\
357 ... [1]358 ... [1]
358 ... consumer_key: aardvark359 ... consumer_key: aardvark
359 ... consumer_secret: badger360 ... consumer_secret: badger
@@ -363,7 +364,7 @@ an error is raised.
363 >>> credentials.load(credentials_file)364 >>> credentials.load(credentials_file)
364 Traceback (most recent call last):365 Traceback (most recent call last):
365 ...366 ...
366 NoOptionError: No option 'access_secret' in section: '1'367 configparser.NoOptionError: No option 'access_secret' in section: '1'
367368
368369
369Bad credentials370Bad credentials
@@ -378,7 +379,7 @@ The application is not allowed to access Launchpad with a bad access token.
378 >>> launchpad = Launchpad(credentials=credentials)379 >>> launchpad = Launchpad(credentials=credentials)
379 Traceback (most recent call last):380 Traceback (most recent call last):
380 ...381 ...
381 Unauthorized: HTTP Error 401: Unauthorized382 lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized
382 ...383 ...
383384
384The application is not allowed to access Launchpad with a consumer385The application is not allowed to access Launchpad with a consumer
@@ -391,7 +392,7 @@ name that doesn't match the credentials.
391 >>> launchpad = Launchpad(credentials=credentials)392 >>> launchpad = Launchpad(credentials=credentials)
392 Traceback (most recent call last):393 Traceback (most recent call last):
393 ...394 ...
394 Unauthorized: HTTP Error 401: Unauthorized395 lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized
395 ...396 ...
396397
397The application is not allowed to access Launchpad with a bad access secret.398The application is not allowed to access Launchpad with a bad access secret.
@@ -403,7 +404,7 @@ The application is not allowed to access Launchpad with a bad access secret.
403 >>> launchpad = Launchpad(credentials=credentials)404 >>> launchpad = Launchpad(credentials=credentials)
404 Traceback (most recent call last):405 Traceback (most recent call last):
405 ...406 ...
406 Unauthorized: HTTP Error 401: Unauthorized407 lazr.restfulclient.errors.Unauthorized: HTTP Error 401: Unauthorized
407 ...408 ...
408409
409Clean up410Clean up
diff --git a/src/launchpadlib/docs/people.rst b/src/launchpadlib/docs/people.rst
index d1dc629..83a98c8 100644
--- a/src/launchpadlib/docs/people.rst
+++ b/src/launchpadlib/docs/people.rst
@@ -21,29 +21,29 @@ The list of people is available from the service root.
2121
22The list of people is not fetched until you actually use data.22The list of people is not fetched until you actually use data.
2323
24 >>> print people._wadl_resource.representation24 >>> print(people._wadl_resource.representation)
25 None25 None
2626
27 >>> len(people)27 >>> len(people)
28 428 4
2929
30 >>> print people._wadl_resource.representation30 >>> print(people._wadl_resource.representation)
31 {...}31 {...}
3232
33The 'me' attribute is also available from the service root. It's a33The 'me' attribute is also available from the service root. It's a
34quick way to get a reference to your own user account.34quick way to get a reference to your own user account.
3535
36 >>> me = launchpad.me36 >>> me = launchpad.me
37 >>> me.name37 >>> print(me.name)
38 u'salgado'38 salgado
3939
40You can find a person by name.40You can find a person by name.
4141
42 >>> salgado = launchpad.people['salgado']42 >>> salgado = launchpad.people['salgado']
43 >>> salgado.name43 >>> print(salgado.name)
44 u'salgado'44 salgado
45 >>> salgado.display_name45 >>> print(salgado.display_name)
46 u'Guilherme Salgado'46 Guilherme Salgado
47 >>> salgado.is_team47 >>> salgado.is_team
48 False48 False
4949
@@ -67,8 +67,8 @@ You can find a person by email.
6767
68 >>> email = salgado.preferred_email_address.email68 >>> email = salgado.preferred_email_address.email
69 >>> salgado = launchpad.people.getByEmail(email=email)69 >>> salgado = launchpad.people.getByEmail(email=email)
70 >>> salgado.name70 >>> print(salgado.name)
71 u'salgado'71 salgado
7272
73Besides a name and a display name, a person has many other attributes that you73Besides a name and a display name, a person has many other attributes that you
74can read.74can read.
@@ -79,7 +79,7 @@ can read.
7979
80 >>> salgado.karma80 >>> salgado.karma
81 081 0
82 >>> print salgado.homepage_content82 >>> print(salgado.homepage_content)
83 None83 None
84 >>> #salgado.mugshot84 >>> #salgado.mugshot
85 >>> #salgado.languages85 >>> #salgado.languages
@@ -87,7 +87,7 @@ can read.
87 False87 False
88 >>> salgado.date_created88 >>> salgado.date_created
89 datetime.datetime(2005, 6, 6, 8, 59, 51, 596025, ...)89 datetime.datetime(2005, 6, 6, 8, 59, 51, 596025, ...)
90 >>> print salgado.time_zone90 >>> print(salgado.time_zone)
91 UTC91 UTC
92 >>> salgado.is_valid92 >>> salgado.is_valid
93 True93 True
@@ -100,10 +100,10 @@ can read.
100 >>> #salgado.teams_indirectly_participated_in100 >>> #salgado.teams_indirectly_participated_in
101 >>> #salgado.confirmed_email_addresses101 >>> #salgado.confirmed_email_addresses
102 >>> #salgado.preferred_email_address102 >>> #salgado.preferred_email_address
103 >>> salgado.mailing_list_auto_subscribe_policy103 >>> print(salgado.mailing_list_auto_subscribe_policy)
104 u'Ask me when I join a team'104 Ask me when I join a team
105 >>> salgado.visibility105 >>> print(salgado.visibility)
106 u'Public'106 Public
107107
108108
109Teams109Teams
@@ -112,16 +112,16 @@ Teams
112You also access teams using the same interface.112You also access teams using the same interface.
113113
114 >>> team = launchpad.people['ubuntu-team']114 >>> team = launchpad.people['ubuntu-team']
115 >>> team.name115 >>> print(team.name)
116 u'ubuntu-team'116 ubuntu-team
117 >>> team.display_name117 >>> print(team.display_name)
118 u'Ubuntu Team'118 Ubuntu Team
119 >>> team.is_team119 >>> team.is_team
120 True120 True
121121
122Regular people have team attributes, but they're not used.122Regular people have team attributes, but they're not used.
123123
124 >>> print salgado.team_owner124 >>> print(salgado.team_owner)
125 None125 None
126126
127You can find out how a person has membership in a team.127You can find out how a person has membership in a team.
@@ -143,27 +143,27 @@ this requires only the new team's name, owner and display name.
143143
144 >>> bassists = launchpad.people.newTeam(144 >>> bassists = launchpad.people.newTeam(
145 ... name='bassists', display_name='Awesome Rock Bass Players')145 ... name='bassists', display_name='Awesome Rock Bass Players')
146 >>> bassists.name146 >>> print(bassists.name)
147 u'bassists'147 bassists
148 >>> bassists.display_name148 >>> print(bassists.display_name)
149 u'Awesome Rock Bass Players'149 Awesome Rock Bass Players
150 >>> bassists.is_team150 >>> bassists.is_team
151 True151 True
152152
153And of course, that team is now accessible directly.153And of course, that team is now accessible directly.
154154
155 >>> bassists = launchpad.people['bassists']155 >>> bassists = launchpad.people['bassists']
156 >>> bassists.name156 >>> print(bassists.name)
157 u'bassists'157 bassists
158 >>> bassists.display_name158 >>> print(bassists.display_name)
159 u'Awesome Rock Bass Players'159 Awesome Rock Bass Players
160160
161You cannot create the same team twice.161You cannot create the same team twice.
162162
163 >>> launchpad.people.newTeam(name='bassists', display_name='Bass Gods')163 >>> launchpad.people.newTeam(name='bassists', display_name='Bass Gods')
164 Traceback (most recent call last):164 Traceback (most recent call last):
165 ...165 ...
166 BadRequest: HTTP Error 400: Bad Request166 lazr.restfulclient.errors.BadRequest: HTTP Error 400: Bad Request
167 ...167 ...
168168
169Actually, the exception contains other useful information.169Actually, the exception contains other useful information.
@@ -172,25 +172,25 @@ Actually, the exception contains other useful information.
172 >>> try:172 >>> try:
173 ... launchpad.people.newTeam(173 ... launchpad.people.newTeam(
174 ... name='bassists', display_name='Bass Gods')174 ... name='bassists', display_name='Bass Gods')
175 ... except HTTPError, error:175 ... except HTTPError as e:
176 ... pass176 ... error = e
177 >>> error.response['status']177 >>> error.response['status']
178 '400'178 '400'
179 >>> error.content179 >>> print(error.content.decode())
180 'name: bassists is already in use by another person or team.'180 name: bassists is already in use by another person or team.
181181
182Besides a name and a display name, a team has many other attributes that you182Besides a name and a display name, a team has many other attributes that you
183can read.183can read.
184184
185 >>> bassists.karma185 >>> bassists.karma
186 0186 0
187 >>> print bassists.homepage_content187 >>> print(bassists.homepage_content)
188 None188 None
189 >>> bassists.hide_email_addresses189 >>> bassists.hide_email_addresses
190 False190 False
191 >>> bassists.date_created191 >>> bassists.date_created
192 datetime.datetime(...)192 datetime.datetime(...)
193 >>> print bassists.time_zone193 >>> print(bassists.time_zone)
194 UTC194 UTC
195 >>> bassists.is_valid195 >>> bassists.is_valid
196 True196 True
@@ -209,15 +209,15 @@ can read.
209 >>> #bassists.invited_members209 >>> #bassists.invited_members
210 >>> #bassists.member_memberships210 >>> #bassists.member_memberships
211 >>> #bassists.proposed_members211 >>> #bassists.proposed_members
212 >>> bassists.visibility212 >>> print(bassists.visibility)
213 u'Public'213 Public
214 >>> print bassists.team_description214 >>> print(bassists.team_description)
215 None215 None
216 >>> bassists.subscription_policy216 >>> print(bassists.subscription_policy)
217 u'Moderated Team'217 Moderated Team
218 >>> bassists.renewal_policy218 >>> print(bassists.renewal_policy)
219 u'invite them to apply for renewal'219 invite them to apply for renewal
220 >>> print bassists.default_membership_period220 >>> print(bassists.default_membership_period)
221 None221 None
222 >>> print bassists.default_renewal_period222 >>> print(bassists.default_renewal_period)
223 None223 None
diff --git a/src/launchpadlib/docs/toplevel.rst b/src/launchpadlib/docs/toplevel.rst
index 76d2a56..67c480a 100644
--- a/src/launchpadlib/docs/toplevel.rst
+++ b/src/launchpadlib/docs/toplevel.rst
@@ -10,14 +10,14 @@ Launchpad-wide objects like projects and people.
1010
11 >>> from launchpadlib.testing.helpers import salgado_with_full_permissions11 >>> from launchpadlib.testing.helpers import salgado_with_full_permissions
12 >>> launchpad = salgado_with_full_permissions.login()12 >>> launchpad = salgado_with_full_permissions.login()
13 connect: ...13 send: ...
14 ...14 ...
1515
16It's possible to do key-based lookups on the top-level16It's possible to do key-based lookups on the top-level
17collections. The bug collection does lookups by bug ID.17collections. The bug collection does lookups by bug ID.
1818
19 >>> bug = launchpad.bugs[1]19 >>> bug = launchpad.bugs[1]
20 send: 'GET /.../bugs/1 ...'20 send: b'GET /.../bugs/1 ...'
21 ...21 ...
2222
23To avoid triggering an HTTP request when simply looking up an object,23To avoid triggering an HTTP request when simply looking up an object,
@@ -28,8 +28,8 @@ you can use a different syntax:
28The HTTP request will happen when you need information that can only28The HTTP request will happen when you need information that can only
29be obtained from the web service.29be obtained from the web service.
3030
31 >>> print bug.id31 >>> print(bug.id)
32 send: 'GET /.../bugs/1 ...'32 send: b'GET /.../bugs/1 ...'
33 ...33 ...
34 134 1
3535
@@ -37,24 +37,24 @@ Let's look at some more collections. The project collection does
37lookups by project name.37lookups by project name.
3838
39 >>> project = launchpad.projects('firefox')39 >>> project = launchpad.projects('firefox')
40 >>> print project.name40 >>> print(project.name)
41 send: 'GET /.../firefox ...'41 send: b'GET /.../firefox ...'
42 ...42 ...
43 firefox43 firefox
4444
45The project group collection does lookups by project group name.45The project group collection does lookups by project group name.
4646
47 >>> group = launchpad.project_groups('gnome')47 >>> group = launchpad.project_groups('gnome')
48 >>> print group.name48 >>> print(group.name)
49 send: 'GET /.../gnome ...'49 send: b'GET /.../gnome ...'
50 ...50 ...
51 gnome51 gnome
5252
53The distribution collection does lookups by distribution name.53The distribution collection does lookups by distribution name.
5454
55 >>> distribution = launchpad.distributions('ubuntu')55 >>> distribution = launchpad.distributions('ubuntu')
56 >>> print distribution.name56 >>> print(distribution.name)
57 send: 'GET /.../ubuntu ...'57 send: b'GET /.../ubuntu ...'
58 ...58 ...
59 ubuntu59 ubuntu
6060
@@ -62,26 +62,26 @@ The person collection does lookups by a person's Launchpad
62name.62name.
6363
64 >>> person = launchpad.people('salgado')64 >>> person = launchpad.people('salgado')
65 >>> print person.name65 >>> print(person.name)
66 send: 'GET /.../~salgado ...'66 send: b'GET /.../~salgado ...'
67 ...67 ...
68 salgado68 salgado
6969
70 >>> team = launchpad.people('rosetta-admins')70 >>> team = launchpad.people('rosetta-admins')
71 >>> print team.name71 >>> print(team.name)
72 send: 'GET /1.0/~rosetta-admins ...'72 send: b'GET /1.0/~rosetta-admins ...'
73 ...73 ...
74 rosetta-admins74 rosetta-admins
7575
76How does launchpadlib know that 'salgado' is a person and76How does launchpadlib know that 'salgado' is a person and
77'rosetta-admins' is a team?77'rosetta-admins' is a team?
7878
79 >>> print person.resource_type_link79 >>> print(person.resource_type_link)
80 http://.../1.0/#person80 http://.../1.0/#person
81 >>> 'default_membership_period' in person.lp_attributes81 >>> 'default_membership_period' in person.lp_attributes
82 False82 False
8383
84 >>> print team.resource_type_link84 >>> print(team.resource_type_link)
85 http://.../1.0/#team85 http://.../1.0/#team
86 >>> 'default_membership_period' in team.lp_attributes86 >>> 'default_membership_period' in team.lp_attributes
87 True87 True
@@ -99,8 +99,8 @@ But accessing any attribute of an object--even trying to see what kind
99of object 'salgado' is--will trigger the HTTP request that will99of object 'salgado' is--will trigger the HTTP request that will
100determine that 'salgado' is actually a person.100determine that 'salgado' is actually a person.
101101
102 >>> print person2.resource_type_link102 >>> print(person2.resource_type_link)
103 send: 'GET /.../~salgado ...'103 send: b'GET /.../~salgado ...'
104 ...104 ...
105 http://.../1.0/#person105 http://.../1.0/#person
106106
@@ -118,4 +118,4 @@ to be a team.
118118
119Cleanup.119Cleanup.
120120
121 >>> httplib2.debuglevel = None121 >>> httplib2.debuglevel = 0

Subscribers

People subscribed via source and target branches