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

Subscribers

People subscribed via source and target branches