Merge ~cjwatson/launchpad:testing-print-function into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 1724d19e4228a63c404e32d4df2bd1d8e926a01f
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:testing-print-function
Merge into: launchpad:master
Diff against target: 761 lines (+126/-107)
13 files modified
lib/lp/testing/__init__.py (+8/-8)
lib/lp/testing/doc/pagetest-helpers.txt (+35/-35)
lib/lp/testing/doc/sample-data-assertions.txt (+1/-1)
lib/lp/testing/faketransaction.py (+3/-1)
lib/lp/testing/karma.py (+3/-1)
lib/lp/testing/layers.py (+4/-2)
lib/lp/testing/mail_helpers.py (+14/-12)
lib/lp/testing/menu.py (+5/-3)
lib/lp/testing/pages.py (+37/-35)
lib/lp/testing/publication.py (+7/-5)
lib/lp/testing/systemdocs.py (+4/-2)
lib/lp/testing/tests/test_doc.py (+2/-1)
lib/lp/testing/yuixhr.py (+3/-1)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Kristian Glass (community) Approve
Review via email: mp+373699@code.launchpad.net

Commit message

Convert lp.testing to print_function

I also added unicode_literals in a few places where it could be done
with minimal disruption.

The build_test_suite change is because for doctests we can't just do
"from __future__ import print_function" etc. at the top of the file, and
instead have to insert print_function etc. into their globals when
constructing the corresponding test suite; this is a way of spelling
that without having to incur too much verbiage at every site.

To post a comment you must log in.
Revision history for this message
Kristian Glass (doismellburning) wrote :

Looks fab, thanks - generally approve but I'd appreciate context on the build_test_suite change please

Revision history for this message
Colin Watson (cjwatson) wrote :

I think the best context for the build_test_suite change is probably to look at the end of https://code.launchpad.net/~cjwatson/launchpad/code-doctests-future-imports/+merge/345470. Briefly, for doctests we can't just do "from __future__ import print_function" etc. at the top of the file; instead, we have to do a much more awkward construction of inserting print_function etc. into their globals when constructing the corresponding test suite. lp.testing.systemdocs.setUp(future=True) is a way of spelling that without having to incur too much verbiage at every site.

Revision history for this message
Kristian Glass (doismellburning) wrote :

Great, thank you

What do you think about adding that context to the commit message? I approve the change either way, but I know if I found myself hitting that line in `git blame`, I'd appreciate the additional explanation

review: Approve
Revision history for this message
Colin Watson (cjwatson) wrote :

Sure - I've amended the commit message.

Revision history for this message
Otto Co-Pilot (otto-copilot) wrote :
Revision history for this message
Colin Watson (cjwatson) wrote :

Self-approving to work around incorrect reviewer on this MP (since fixed).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index 81e0f1f..5f91c86 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -1,7 +1,7 @@
1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the1# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4from __future__ import absolute_import4from __future__ import absolute_import, print_function
55
66
7__metaclass__ = type7__metaclass__ = type
@@ -258,21 +258,21 @@ class FakeTime:
258 calls of advance() or next_now().258 calls of advance() or next_now().
259259
260 >>> faketime = FakeTime(1000)260 >>> faketime = FakeTime(1000)
261 >>> print faketime.now()261 >>> print(faketime.now())
262 1000262 1000
263 >>> print faketime.now()263 >>> print(faketime.now())
264 1000264 1000
265 >>> faketime.advance(10)265 >>> faketime.advance(10)
266 >>> print faketime.now()266 >>> print(faketime.now())
267 1010267 1010
268 >>> print faketime.next_now()268 >>> print(faketime.next_now())
269 1011269 1011
270 >>> print faketime.next_now(100)270 >>> print(faketime.next_now(100))
271 1111271 1111
272 >>> faketime = FakeTime(1000, 5)272 >>> faketime = FakeTime(1000, 5)
273 >>> print faketime.next_now()273 >>> print(faketime.next_now())
274 1005274 1005
275 >>> print faketime.next_now()275 >>> print(faketime.next_now())
276 1010276 1010
277 """277 """
278278
diff --git a/lib/lp/testing/doc/pagetest-helpers.txt b/lib/lp/testing/doc/pagetest-helpers.txt
index e0bb718..21b6e8f 100644
--- a/lib/lp/testing/doc/pagetest-helpers.txt
+++ b/lib/lp/testing/doc/pagetest-helpers.txt
@@ -30,32 +30,32 @@ one should be use for all anonymous browsing tests.
30 ... return dict(browser.mech_browser.addheaders).get('Authorization','')30 ... return dict(browser.mech_browser.addheaders).get('Authorization','')
3131
32 >>> anon_browser = test.globs['anon_browser']32 >>> anon_browser = test.globs['anon_browser']
33 >>> getAuthorizationHeader(anon_browser)33 >>> print(getAuthorizationHeader(anon_browser))
34 ''34 <BLANKLINE>
3535
36A browser with a logged in user without any privileges is available36A browser with a logged in user without any privileges is available
37under 'user_browser'. This one should be use all workflows involving37under 'user_browser'. This one should be use all workflows involving
38logged in users, when it shouldn't require any special privileges.38logged in users, when it shouldn't require any special privileges.
3939
40 >>> user_browser = test.globs['user_browser']40 >>> user_browser = test.globs['user_browser']
41 >>> getAuthorizationHeader(user_browser)41 >>> print(getAuthorizationHeader(user_browser))
42 'Basic no-priv@canonical.com:test'42 Basic no-priv@canonical.com:test
4343
44A browser with a logged in user with administrative privileges is44A browser with a logged in user with administrative privileges is
45available under 'admin_browser'. This one should be used for testing45available under 'admin_browser'. This one should be used for testing
46administrative workflows.46administrative workflows.
4747
48 >>> admin_browser = test.globs['admin_browser']48 >>> admin_browser = test.globs['admin_browser']
49 >>> getAuthorizationHeader(admin_browser)49 >>> print(getAuthorizationHeader(admin_browser))
50 'Basic foo.bar@canonical.com:test'50 Basic foo.bar@canonical.com:test
5151
52Finally, here is a 'browser' instance that simply contain a pre-52Finally, here is a 'browser' instance that simply contain a pre-
53initialized Browser instance. It doesn't have any authentication53initialized Browser instance. It doesn't have any authentication
54configured. It can be used when you need to configure another user.54configured. It can be used when you need to configure another user.
5555
56 >>> browser = test.globs['browser']56 >>> browser = test.globs['browser']
57 >>> getAuthorizationHeader(browser)57 >>> print(getAuthorizationHeader(browser))
58 ''58 <BLANKLINE>
5959
60All these browser instances are configured with handleErrors set to60All these browser instances are configured with handleErrors set to
61False. This means that exception are raised instead of returning the61False. This means that exception are raised instead of returning the
@@ -111,15 +111,15 @@ This routine will return the tag with the given id:
111 ... </html>111 ... </html>
112 ... '''112 ... '''
113113
114 >>> print find_tag_by_id(content, 'para-1')114 >>> print(find_tag_by_id(content, 'para-1'))
115 <p id="para-1">Paragraph 1</p>115 <p id="para-1">Paragraph 1</p>
116116
117 >>> print find_tag_by_id(content, 'para-2')117 >>> print(find_tag_by_id(content, 'para-2'))
118 <p id="para-2">Paragraph <b>2</b></p>118 <p id="para-2">Paragraph <b>2</b></p>
119119
120If an unknown ID is used, None is returned:120If an unknown ID is used, None is returned:
121121
122 >>> print find_tag_by_id(content, 'para-3')122 >>> print(find_tag_by_id(content, 'para-3'))
123 None123 None
124124
125If more than one element has the requested id, raise a DuplicateIdError125If more than one element has the requested id, raise a DuplicateIdError
@@ -140,10 +140,10 @@ A BeautifulSoup PageElement can be passed instead of a string so that
140content can be retrieved without reparsing the entire page.140content can be retrieved without reparsing the entire page.
141141
142 >>> parsed_content = find_tag_by_id(content, 'root')142 >>> parsed_content = find_tag_by_id(content, 'root')
143 >>> print parsed_content.name143 >>> print(parsed_content.name)
144 html144 html
145145
146 >>> print find_tag_by_id(parsed_content, 'para-1')146 >>> print(find_tag_by_id(parsed_content, 'para-1'))
147 <p id="para-1">Paragraph 1</p>147 <p id="para-1">Paragraph 1</p>
148148
149149
@@ -172,18 +172,18 @@ class:
172 ... '''172 ... '''
173173
174 >>> for tag in find_tags_by_class(content, 'message'):174 >>> for tag in find_tags_by_class(content, 'message'):
175 ... print tag175 ... print(tag)
176 <p class="message">Message</p>176 <p class="message">Message</p>
177 <p class="error message">Error message</p>177 <p class="error message">Error message</p>
178 <p class="warning message">Warning message</p>178 <p class="warning message">Warning message</p>
179179
180 >>> for tag in find_tags_by_class(content, 'error'):180 >>> for tag in find_tags_by_class(content, 'error'):
181 ... print tag181 ... print(tag)
182 <p class="error message">Error message</p>182 <p class="error message">Error message</p>
183 <p class="error">Error</p>183 <p class="error">Error</p>
184184
185 >>> for tag in find_tags_by_class(content, 'warning'):185 >>> for tag in find_tags_by_class(content, 'warning'):
186 ... print tag186 ... print(tag)
187 <p class="warning message">Warning message</p>187 <p class="warning message">Warning message</p>
188 <p class="warning">188 <p class="warning">
189 Warning (outer)189 Warning (outer)
@@ -218,7 +218,7 @@ matching Tag object, if one exists:
218 ... </html>218 ... </html>
219 ... '''219 ... '''
220220
221 >>> print first_tag_by_class(content, 'light')221 >>> print(first_tag_by_class(content, 'light'))
222 <p class="light">Error message</p>222 <p class="light">Error message</p>
223223
224If no tags have the given class, then "None" is returned.224If no tags have the given class, then "None" is returned.
@@ -235,7 +235,7 @@ If no tags have the given class, then "None" is returned.
235 ... </html>235 ... </html>
236 ... '''236 ... '''
237237
238 >>> print first_tag_by_class(content, 'light')238 >>> print(first_tag_by_class(content, 'light'))
239 None239 None
240240
241241
@@ -276,11 +276,11 @@ find a portlet by its title and return it:
276 ... </html>276 ... </html>
277 ... '''277 ... '''
278278
279 >>> print find_portlet(content, 'Portlet 1')279 >>> print(find_portlet(content, 'Portlet 1'))
280 <div...280 <div...
281 ...Contents of portlet 1...281 ...Contents of portlet 1...
282282
283 >>> print find_portlet(content, 'Portlet 2')283 >>> print(find_portlet(content, 'Portlet 2'))
284 <div class="portlet">284 <div class="portlet">
285 <h2>Portlet 2</h2>285 <h2>Portlet 2</h2>
286 Contents of portlet 2286 Contents of portlet 2
@@ -290,14 +290,14 @@ When looking for a portlet to match, any two sequences of whitespace are
290considered equivalent. Whitespace at the beginning or end of the title290considered equivalent. Whitespace at the beginning or end of the title
291is also ignored.291is also ignored.
292292
293 >>> print find_portlet(293 >>> print(find_portlet(
294 ... content, 'Portlet with title broken on multiple lines ')294 ... content, 'Portlet with title broken on multiple lines '))
295 <div class="portlet">295 <div class="portlet">
296 <h2> Portlet with title...296 <h2> Portlet with title...
297297
298If the portlet doesn't exist, then None is returned:298If the portlet doesn't exist, then None is returned:
299299
300 >>> print find_portlet(content, 'No such portlet')300 >>> print(find_portlet(content, 'No such portlet'))
301 None301 None
302302
303303
@@ -309,7 +309,7 @@ the main content of the page. The find_main_content() method can be
309used to do this:309used to do this:
310310
311 >>> find_main_content = test.globs['find_main_content']311 >>> find_main_content = test.globs['find_main_content']
312 >>> print find_main_content(content)312 >>> print(find_main_content(content))
313 <...313 <...
314 Main content area314 Main content area
315 ...315 ...
@@ -323,16 +323,16 @@ to the end user, and we don't want necessarily to check how the text is
323displayed (ie. bold, italics, coloured et al).323displayed (ie. bold, italics, coloured et al).
324324
325 >>> extract_text = test.globs['extract_text']325 >>> extract_text = test.globs['extract_text']
326 >>> print extract_text(326 >>> print(extract_text(
327 ... '<p>A paragraph with <b>inline</b> <i>style</i>.</p>')327 ... '<p>A paragraph with <b>inline</b> <i>style</i>.</p>'))
328 A paragraph with inline style.328 A paragraph with inline style.
329329
330The function also takes care of inserting proper white space for block330The function also takes care of inserting proper white space for block
331level and other elements introducing a visual separation:331level and other elements introducing a visual separation:
332332
333 >>> print extract_text( # doctest: -NORMALIZE_WHITESPACE333 >>> print(extract_text( # doctest: -NORMALIZE_WHITESPACE
334 ... '<p>Para 1</p><p>Para 2<br>Line 2</p><ul><li>Item 1</li>'334 ... '<p>Para 1</p><p>Para 2<br>Line 2</p><ul><li>Item 1</li>'
335 ... '<li>Item 2</li></ul><div>Div 1</div><h1>A heading</h1>')335 ... '<li>Item 2</li></ul><div>Div 1</div><h1>A heading</h1>'))
336 Para 1336 Para 1
337 Para 2337 Para 2
338 Line 2338 Line 2
@@ -344,9 +344,9 @@ level and other elements introducing a visual separation:
344Of course, the function ignores processing instructions, declaration,344Of course, the function ignores processing instructions, declaration,
345comments and render CDATA section has plain text.345comments and render CDATA section has plain text.
346346
347 >>> print extract_text(347 >>> print(extract_text(
348 ... '<?php echo("Hello world!")?><!-- A comment -->'348 ... '<?php echo("Hello world!")?><!-- A comment -->'
349 ... '<?A declaration.><![CDATA[Some << characters >>]]>')349 ... '<?A declaration.><![CDATA[Some << characters >>]]>'))
350 Some << characters >>350 Some << characters >>
351351
352The function also does some white space normalization, since formatted352The function also does some white space normalization, since formatted
@@ -358,10 +358,10 @@ single space. Runs of newlines is replaced by one newline. (Note also
358that non-breaking space entities are also transformed into regular358that non-breaking space entities are also transformed into regular
359space.)359space.)
360360
361 >>> print extract_text( # doctest: -NORMALIZE_WHITESPACE361 >>> print(extract_text( # doctest: -NORMALIZE_WHITESPACE
362 ... ' <p>Some \t white space <br /></p> '362 ... ' <p>Some \t white space <br /></p> '
363 ... '<p>Another&nbsp; &#160; paragraph.</p><p><p>'363 ... '<p>Another&nbsp; &#160; paragraph.</p><p><p>'
364 ... '<p>A final one</p> ')364 ... '<p>A final one</p> '))
365 Some white space365 Some white space
366 Another paragraph.366 Another paragraph.
367 A final one367 A final one
@@ -369,10 +369,10 @@ space.)
369The function also knows about the sortkey class used in many tables. The369The function also knows about the sortkey class used in many tables. The
370sortkey is not displayed but is used for the javascript table sorting.370sortkey is not displayed but is used for the javascript table sorting.
371371
372 >>> print extract_text(372 >>> print(extract_text(
373 ... '<table><tr><td><span class="sortkey">1</span>First</td></tr>'373 ... '<table><tr><td><span class="sortkey">1</span>First</td></tr>'
374 ... '<tr><td><span class="sortkey">2</span>Second</td></tr>'374 ... '<tr><td><span class="sortkey">2</span>Second</td></tr>'
375 ... '<tr><td><span class="sortkey">3</span>Third</td></tr></table>')375 ... '<tr><td><span class="sortkey">3</span>Third</td></tr></table>'))
376 First Second Third376 First Second Third
377377
378The extract_text method is often used in conjunction with the other378The extract_text method is often used in conjunction with the other
@@ -380,7 +380,7 @@ find_xxx helper methods to identify the text to display. Because of
380this the function also accepts BeautifulSoup instance as a parameter380this the function also accepts BeautifulSoup instance as a parameter
381rather than a plain string.381rather than a plain string.
382382
383 >>> print extract_text(find_portlet(content, 'Portlet 2'))383 >>> print(extract_text(find_portlet(content, 'Portlet 2')))
384 Portlet 2384 Portlet 2
385 Contents of portlet 2385 Contents of portlet 2
386386
diff --git a/lib/lp/testing/doc/sample-data-assertions.txt b/lib/lp/testing/doc/sample-data-assertions.txt
index 7f9548e..d50015c 100644
--- a/lib/lp/testing/doc/sample-data-assertions.txt
+++ b/lib/lp/testing/doc/sample-data-assertions.txt
@@ -29,5 +29,5 @@ specifically referenced in Launchpad tests.
29 This user is supposed to be a member of only one team, the "Simple Team".29 This user is supposed to be a member of only one team, the "Simple Team".
3030
31 >>> one_membership = personset.getByName('one-membership')31 >>> one_membership = personset.getByName('one-membership')
32 >>> for t in one_membership.team_memberships: print t.team.displayname32 >>> for t in one_membership.team_memberships: print(t.team.displayname)
33 Simple Team33 Simple Team
diff --git a/lib/lp/testing/faketransaction.py b/lib/lp/testing/faketransaction.py
index 8bf2386..1c41916 100644
--- a/lib/lp/testing/faketransaction.py
+++ b/lib/lp/testing/faketransaction.py
@@ -3,6 +3,8 @@
33
4"""Fake transaction manager."""4"""Fake transaction manager."""
55
6from __future__ import absolute_import, print_function, unicode_literals
7
6__metaclass__ = type8__metaclass__ = type
7__all__ = ['FakeTransaction']9__all__ = ['FakeTransaction']
810
@@ -23,7 +25,7 @@ class FakeTransaction:
23 def _log(self, call):25 def _log(self, call):
24 """Print calls that are being made, if desired."""26 """Print calls that are being made, if desired."""
25 if self.log_calls:27 if self.log_calls:
26 print call28 print(call)
2729
28 def begin(self):30 def begin(self):
29 """Pretend to begin a transaction. Does not log."""31 """Pretend to begin a transaction. Does not log."""
diff --git a/lib/lp/testing/karma.py b/lib/lp/testing/karma.py
index d8b2ea3..c0eaf8a 100644
--- a/lib/lp/testing/karma.py
+++ b/lib/lp/testing/karma.py
@@ -3,6 +3,8 @@
33
4"""Helper functions/classes to be used when testing the karma framework."""4"""Helper functions/classes to be used when testing the karma framework."""
55
6from __future__ import absolute_import, print_function, unicode_literals
7
6__metaclass__ = type8__metaclass__ = type
7__all__ = [9__all__ = [
8 'KarmaAssignedEventListener',10 'KarmaAssignedEventListener',
@@ -123,4 +125,4 @@ class KarmaAssignedEventListener(KarmaRecorder):
123 text += " distribution=%s" % karma.distribution.name125 text += " distribution=%s" % karma.distribution.name
124 if self.show_person:126 if self.show_person:
125 text += ", person=%s" % karma.person.name127 text += ", person=%s" % karma.person.name
126 print text128 print(text)
diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
index ffac753..647fbb1 100644
--- a/lib/lp/testing/layers.py
+++ b/lib/lp/testing/layers.py
@@ -18,6 +18,8 @@ of one, forcing us to attempt to make some sort of layer tree.
18-- StuartBishop 2006061918-- StuartBishop 20060619
19"""19"""
2020
21from __future__ import absolute_import, print_function
22
21__metaclass__ = type23__metaclass__ = type
22__all__ = [24__all__ = [
23 'AppServerLayer',25 'AppServerLayer',
@@ -447,9 +449,9 @@ class BaseLayer:
447 # tests that leave threads behind from failing. Its use449 # tests that leave threads behind from failing. Its use
448 # should only ever be temporary.450 # should only ever be temporary.
449 if BaseLayer.disable_thread_check:451 if BaseLayer.disable_thread_check:
450 print (452 print((
451 "ERROR DISABLED: "453 "ERROR DISABLED: "
452 "Test left new live threads: %s") % repr(new_threads)454 "Test left new live threads: %s") % repr(new_threads))
453 else:455 else:
454 BaseLayer.flagTestIsolationFailure(456 BaseLayer.flagTestIsolationFailure(
455 "Test left new live threads: %s" % repr(new_threads))457 "Test left new live threads: %s" % repr(new_threads))
diff --git a/lib/lp/testing/mail_helpers.py b/lib/lp/testing/mail_helpers.py
index e5a27a9..3ac740a 100644
--- a/lib/lp/testing/mail_helpers.py
+++ b/lib/lp/testing/mail_helpers.py
@@ -1,8 +1,10 @@
1# Copyright 2009-2019 Canonical Ltd. This software is licensed under the1# Copyright 2009-2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Helper functions dealing with emails in tests.4"""Helper functions dealing with emails in tests."""
5"""5
6from __future__ import absolute_import, print_function
7
6__metaclass__ = type8__metaclass__ = type
79
8import email10import email
@@ -102,23 +104,23 @@ def print_emails(include_reply_to=False, group_similar=False,
102 distinct_bodies[body] = (message, recipients)104 distinct_bodies[body] = (message, recipients)
103 for body in sorted(distinct_bodies):105 for body in sorted(distinct_bodies):
104 message, recipients = distinct_bodies[body]106 message, recipients = distinct_bodies[body]
105 print 'From:', message['From']107 print('From:', message['From'])
106 print 'To:', ", ".join(sorted(recipients))108 print('To:', ", ".join(sorted(recipients)))
107 if include_reply_to:109 if include_reply_to:
108 print 'Reply-To:', message['Reply-To']110 print('Reply-To:', message['Reply-To'])
109 rationale_header = 'X-Launchpad-Message-Rationale'111 rationale_header = 'X-Launchpad-Message-Rationale'
110 if include_rationale and rationale_header in message:112 if include_rationale and rationale_header in message:
111 print '%s: %s' % (rationale_header, message[rationale_header])113 print('%s: %s' % (rationale_header, message[rationale_header]))
112 for_header = 'X-Launchpad-Message-For'114 for_header = 'X-Launchpad-Message-For'
113 if include_for and for_header in message:115 if include_for and for_header in message:
114 print '%s: %s' % (for_header, message[for_header])116 print('%s: %s' % (for_header, message[for_header]))
115 notification_type_header = 'X-Launchpad-Notification-Type'117 notification_type_header = 'X-Launchpad-Notification-Type'
116 if include_notification_type and notification_type_header in message:118 if include_notification_type and notification_type_header in message:
117 print '%s: %s' % (119 print('%s: %s' % (
118 notification_type_header, message[notification_type_header])120 notification_type_header, message[notification_type_header]))
119 print 'Subject:', message['Subject']121 print('Subject:', message['Subject'])
120 print body122 print(body)
121 print "-" * 40123 print("-" * 40)
122124
123125
124def print_distinct_emails(include_reply_to=False, include_rationale=True,126def print_distinct_emails(include_reply_to=False, include_rationale=True,
diff --git a/lib/lp/testing/menu.py b/lib/lp/testing/menu.py
index 43ed314..f0f3676 100644
--- a/lib/lp/testing/menu.py
+++ b/lib/lp/testing/menu.py
@@ -3,6 +3,8 @@
33
4"""Helpers for testing menus."""4"""Helpers for testing menus."""
55
6from __future__ import absolute_import, print_function
7
6__metaclass__ = type8__metaclass__ = type
7__all__ = [9__all__ = [
8 'summarise_tal_links',10 'summarise_tal_links',
@@ -62,14 +64,14 @@ def summarise_tal_links(links):
62 else:64 else:
63 link = key65 link = key
64 if ILink.providedBy(link):66 if ILink.providedBy(link):
65 print 'link %s' % link.name67 print('link %s' % link.name)
66 attributes = ('url', 'enabled', 'menu', 'selected', 'linked')68 attributes = ('url', 'enabled', 'menu', 'selected', 'linked')
67 for attrname in attributes:69 for attrname in attributes:
68 if not safe_hasattr(link, attrname):70 if not safe_hasattr(link, attrname):
69 continue71 continue
70 print ' %s:' % attrname, getattr(link, attrname)72 print(' %s:' % attrname, getattr(link, attrname))
71 else:73 else:
72 print 'attribute %s: %s' % (key, link)74 print('attribute %s: %s' % (key, link))
7375
7476
75def make_fake_request(url, traversed_objects=None):77def make_fake_request(url, traversed_objects=None):
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index b73d004..9e1b0c3 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -3,6 +3,8 @@
33
4"""Testing infrastructure for page tests."""4"""Testing infrastructure for page tests."""
55
6from __future__ import absolute_import, print_function
7
6__metaclass__ = type8__metaclass__ = type
79
8from contextlib import contextmanager10from contextlib import contextmanager
@@ -216,7 +218,7 @@ def find_tag_by_id(content, id):
216 return elements_with_id[0]218 return elements_with_id[0]
217 else:219 else:
218 raise DuplicateIdError(220 raise DuplicateIdError(
219 'Found %d elements with id %r' % (len(elements_with_id), id))221 "Found %d elements with id '%s'" % (len(elements_with_id), id))
220222
221223
222def first_tag_by_class(content, class_):224def first_tag_by_class(content, class_):
@@ -286,7 +288,7 @@ def get_feedback_messages(content):
286def print_feedback_messages(content, formatter='minimal'):288def print_feedback_messages(content, formatter='minimal'):
287 """Print out the feedback messages."""289 """Print out the feedback messages."""
288 for message in get_feedback_messages(content):290 for message in get_feedback_messages(content):
289 print extract_text(message, formatter=formatter)291 print(extract_text(message, formatter=formatter))
290292
291293
292def print_table(content, columns=None, skip_rows=None, sep="\t"):294def print_table(content, columns=None, skip_rows=None, sep="\t"):
@@ -307,7 +309,7 @@ def print_table(content, columns=None, skip_rows=None, sep="\t"):
307 if columns is None or col_num in columns:309 if columns is None or col_num in columns:
308 row_content.append(extract_text(item))310 row_content.append(extract_text(item))
309 if len(row_content) > 0:311 if len(row_content) > 0:
310 print sep.join(row_content)312 print(sep.join(row_content))
311313
312314
313def get_radio_button_text_for_field(soup, name):315def get_radio_button_text_for_field(soup, name):
@@ -340,7 +342,7 @@ def print_radio_button_field(content, name):
340 """342 """
341 main = BeautifulSoup(content)343 main = BeautifulSoup(content)
342 for field in get_radio_button_text_for_field(main, name):344 for field in get_radio_button_text_for_field(main, name):
343 print field345 print(field)
344346
345347
346def strip_label(label):348def strip_label(label):
@@ -474,48 +476,48 @@ def parse_relationship_section(content):
474 section = soup.find('ul')476 section = soup.find('ul')
475 whitespace_re = re.compile('\s+')477 whitespace_re = re.compile('\s+')
476 if section is None:478 if section is None:
477 print 'EMPTY SECTION'479 print('EMPTY SECTION')
478 return480 return
479 for li in section.findAll('li'):481 for li in section.findAll('li'):
480 if li.a:482 if li.a:
481 link = li.a483 link = li.a
482 content = whitespace_re.sub(' ', link.string.strip())484 content = whitespace_re.sub(' ', link.string.strip())
483 url = link['href']485 url = link['href']
484 print 'LINK: "%s" -> %s' % (content, url)486 print('LINK: "%s" -> %s' % (content, url))
485 else:487 else:
486 content = whitespace_re.sub(' ', li.string.strip())488 content = whitespace_re.sub(' ', li.string.strip())
487 print 'TEXT: "%s"' % content489 print('TEXT: "%s"' % content)
488490
489491
490def print_action_links(content):492def print_action_links(content):
491 """Print action menu urls."""493 """Print action menu urls."""
492 actions = find_tag_by_id(content, 'actions')494 actions = find_tag_by_id(content, 'actions')
493 if actions is None:495 if actions is None:
494 print "No actions portlet"496 print("No actions portlet")
495 return497 return
496 entries = actions.findAll('li')498 entries = actions.findAll('li')
497 for entry in entries:499 for entry in entries:
498 if entry.a:500 if entry.a:
499 print '%s: %s' % (entry.a.string, entry.a['href'])501 print('%s: %s' % (entry.a.string, entry.a['href']))
500 elif entry.strong:502 elif entry.strong:
501 print entry.strong.string503 print(entry.strong.string)
502504
503505
504def print_navigation_links(content):506def print_navigation_links(content):
505 """Print navigation menu urls."""507 """Print navigation menu urls."""
506 navigation_links = find_tag_by_id(content, 'navigation-tabs')508 navigation_links = find_tag_by_id(content, 'navigation-tabs')
507 if navigation_links is None:509 if navigation_links is None:
508 print "No navigation links"510 print("No navigation links")
509 return511 return
510 title = navigation_links.find('label')512 title = navigation_links.find('label')
511 if title is not None:513 if title is not None:
512 print '= %s =' % title.string514 print('= %s =' % title.string)
513 entries = navigation_links.findAll(['strong', 'a'])515 entries = navigation_links.findAll(['strong', 'a'])
514 for entry in entries:516 for entry in entries:
515 try:517 try:
516 print '%s: %s' % (entry.span.string, entry['href'])518 print('%s: %s' % (entry.span.string, entry['href']))
517 except KeyError:519 except KeyError:
518 print entry.span.string520 print(entry.span.string)
519521
520522
521def print_portlet_links(content, name, base=None):523def print_portlet_links(content, name, base=None):
@@ -536,15 +538,15 @@ def print_portlet_links(content, name, base=None):
536538
537 portlet_contents = find_portlet(content, name)539 portlet_contents = find_portlet(content, name)
538 if portlet_contents is None:540 if portlet_contents is None:
539 print "No portlet found with name:", name541 print("No portlet found with name:", name)
540 return542 return
541 portlet_links = portlet_contents.findAll('a')543 portlet_links = portlet_contents.findAll('a')
542 if len(portlet_links) == 0:544 if len(portlet_links) == 0:
543 print "No links were found in the portlet."545 print("No links were found in the portlet.")
544 return546 return
545 for portlet_link in portlet_links:547 for portlet_link in portlet_links:
546 print '%s: %s' % (portlet_link.string,548 print('%s: %s' % (portlet_link.string,
547 extract_link_from_tag(portlet_link, base))549 extract_link_from_tag(portlet_link, base)))
548550
549551
550def print_submit_buttons(content):552def print_submit_buttons(content):
@@ -555,10 +557,10 @@ def print_submit_buttons(content):
555 buttons = find_main_content(content).findAll(557 buttons = find_main_content(content).findAll(
556 'input', attrs={'class': 'button', 'type': 'submit'})558 'input', attrs={'class': 'button', 'type': 'submit'})
557 if buttons is None:559 if buttons is None:
558 print "No buttons found"560 print("No buttons found")
559 else:561 else:
560 for button in buttons:562 for button in buttons:
561 print button['value']563 print(button['value'])
562564
563565
564def print_comments(page):566def print_comments(page):
@@ -566,31 +568,31 @@ def print_comments(page):
566 main_content = find_main_content(page)568 main_content = find_main_content(page)
567 for comment in main_content('div', 'boardCommentBody'):569 for comment in main_content('div', 'boardCommentBody'):
568 for li_tag in comment('li'):570 for li_tag in comment('li'):
569 print "Attachment: %s" % li_tag.a.renderContents()571 print("Attachment: %s" % li_tag.a.renderContents())
570 print comment.div.renderContents()572 print(comment.div.renderContents())
571 print "-" * 40573 print("-" * 40)
572574
573575
574def print_batch_header(soup):576def print_batch_header(soup):
575 """Print the batch navigator header."""577 """Print the batch navigator header."""
576 navigation = soup.find('td', {'class': 'batch-navigation-index'})578 navigation = soup.find('td', {'class': 'batch-navigation-index'})
577 print extract_text(navigation).encode('ASCII', 'backslashreplace')579 print(extract_text(navigation).encode('ASCII', 'backslashreplace'))
578580
579581
580def print_self_link_of_entries(json_body):582def print_self_link_of_entries(json_body):
581 """Print the self_link attribute of each entry in the given JSON body."""583 """Print the self_link attribute of each entry in the given JSON body."""
582 links = sorted(entry['self_link'] for entry in json_body['entries'])584 links = sorted(entry['self_link'] for entry in json_body['entries'])
583 for link in links:585 for link in links:
584 print link586 print(link)
585587
586588
587def print_ppa_packages(contents):589def print_ppa_packages(contents):
588 packages = find_tags_by_class(contents, 'archive_package_row')590 packages = find_tags_by_class(contents, 'archive_package_row')
589 for pkg in packages:591 for pkg in packages:
590 print extract_text(pkg)592 print(extract_text(pkg))
591 empty_section = find_tag_by_id(contents, 'empty-result')593 empty_section = find_tag_by_id(contents, 'empty-result')
592 if empty_section is not None:594 if empty_section is not None:
593 print extract_text(empty_section)595 print(extract_text(empty_section))
594596
595597
596def print_location(contents):598def print_location(contents):
@@ -614,8 +616,8 @@ def print_location(contents):
614 else:616 else:
615 breadcrumbs = ' > '.join(segments)617 breadcrumbs = ' > '.join(segments)
616618
617 print 'Hierarchy:', breadcrumbs619 print('Hierarchy:', breadcrumbs)
618 print 'Tabs:'620 print('Tabs:')
619 print_location_apps(contents)621 print_location_apps(contents)
620 main_heading = doc.h1622 main_heading = doc.h1
621 if main_heading:623 if main_heading:
@@ -623,7 +625,7 @@ def print_location(contents):
623 'us-ascii', 'replace')625 'us-ascii', 'replace')
624 else:626 else:
625 main_heading = '(No main heading)'627 main_heading = '(No main heading)'
626 print "Main heading: %s" % main_heading628 print("Main heading: %s" % main_heading)
627629
628630
629def print_location_apps(contents):631def print_location_apps(contents):
@@ -636,9 +638,9 @@ def print_location_apps(contents):
636 else:638 else:
637 location_apps = location_apps.findAll('span')639 location_apps = location_apps.findAll('span')
638 if location_apps is None:640 if location_apps is None:
639 print "(Application tabs omitted)"641 print("(Application tabs omitted)")
640 elif len(location_apps) == 0:642 elif len(location_apps) == 0:
641 print "(No application tabs)"643 print("(No application tabs)")
642 else:644 else:
643 for tab in location_apps:645 for tab in location_apps:
644 tab_text = extract_text(tab)646 tab_text = extract_text(tab)
@@ -652,13 +654,13 @@ def print_location_apps(contents):
652 link = tab.a['href']654 link = tab.a['href']
653 else:655 else:
654 link = 'not linked'656 link = 'not linked'
655 print "* %s - %s" % (tab_text, link)657 print("* %s - %s" % (tab_text, link))
656658
657659
658def print_tag_with_id(contents, id):660def print_tag_with_id(contents, id):
659 """A simple helper to print the extracted text of the tag."""661 """A simple helper to print the extracted text of the tag."""
660 tag = find_tag_by_id(contents, id)662 tag = find_tag_by_id(contents, id)
661 print extract_text(tag)663 print(extract_text(tag))
662664
663665
664def print_errors(contents):666def print_errors(contents):
@@ -666,7 +668,7 @@ def print_errors(contents):
666 errors = find_tags_by_class(contents, 'error')668 errors = find_tags_by_class(contents, 'error')
667 error_texts = [extract_text(error) for error in errors]669 error_texts = [extract_text(error) for error in errors]
668 for error in error_texts:670 for error in error_texts:
669 print error671 print(error)
670672
671673
672def setupBrowser(auth=None):674def setupBrowser(auth=None):
diff --git a/lib/lp/testing/publication.py b/lib/lp/testing/publication.py
index 929c643..7f8ee3a 100644
--- a/lib/lp/testing/publication.py
+++ b/lib/lp/testing/publication.py
@@ -3,6 +3,8 @@
33
4"""Helpers for testing out publication related code."""4"""Helpers for testing out publication related code."""
55
6from __future__ import absolute_import, print_function
7
6__metaclass__ = type8__metaclass__ = type
7__all__ = [9__all__ = [
8 'get_request_and_publication',10 'get_request_and_publication',
@@ -67,15 +69,15 @@ def print_request_and_publication(host='localhost', port=None,
67 request, publication = get_request_and_publication(69 request, publication = get_request_and_publication(
68 host, port, method, mime_type,70 host, port, method, mime_type,
69 extra_environment=extra_environment)71 extra_environment=extra_environment)
70 print type(request).__name__.split('.')[-1]72 print(type(request).__name__.split('.')[-1])
71 publication_classname = type(publication).__name__.split('.')[-1]73 publication_classname = type(publication).__name__.split('.')[-1]
72 if isinstance(publication, ProtocolErrorPublication):74 if isinstance(publication, ProtocolErrorPublication):
73 print "%s: status=%d" % (75 print("%s: status=%d" % (
74 publication_classname, publication.status)76 publication_classname, publication.status))
75 for name, value in publication.headers.items():77 for name, value in publication.headers.items():
76 print " %s: %s" % (name, value)78 print(" %s: %s" % (name, value))
77 else:79 else:
78 print publication_classname80 print(publication_classname)
7981
8082
81def test_traverse(url):83def test_traverse(url):
diff --git a/lib/lp/testing/systemdocs.py b/lib/lp/testing/systemdocs.py
index d72d7e7..5a34004 100644
--- a/lib/lp/testing/systemdocs.py
+++ b/lib/lp/testing/systemdocs.py
@@ -3,6 +3,8 @@
33
4"""Infrastructure for setting up doctests."""4"""Infrastructure for setting up doctests."""
55
6from __future__ import absolute_import, print_function
7
6__metaclass__ = type8__metaclass__ = type
7__all__ = [9__all__ = [
8 'default_optionflags',10 'default_optionflags',
@@ -85,8 +87,8 @@ class StdoutHandler(Handler):
85 """87 """
86 def emit(self, record):88 def emit(self, record):
87 Handler.emit(self, record)89 Handler.emit(self, record)
88 print >> sys.stdout, '%s:%s:%s' % (90 print('%s:%s:%s' % (
89 record.levelname, record.name, self.format(record))91 record.levelname, record.name, self.format(record)))
9092
9193
92def LayeredDocFileSuite(paths, id_extensions=None, **kw):94def LayeredDocFileSuite(paths, id_extensions=None, **kw):
diff --git a/lib/lp/testing/tests/test_doc.py b/lib/lp/testing/tests/test_doc.py
index 7ce594c..66783e9 100644
--- a/lib/lp/testing/tests/test_doc.py
+++ b/lib/lp/testing/tests/test_doc.py
@@ -8,10 +8,11 @@ Run the doctests and pagetests.
8import os8import os
99
10from lp.services.testing import build_test_suite10from lp.services.testing import build_test_suite
11from lp.testing.systemdocs import setUp
1112
1213
13here = os.path.dirname(os.path.realpath(__file__))14here = os.path.dirname(os.path.realpath(__file__))
1415
1516
16def test_suite():17def test_suite():
17 return build_test_suite(here)18 return build_test_suite(here, setUp=lambda test: setUp(test, future=True))
diff --git a/lib/lp/testing/yuixhr.py b/lib/lp/testing/yuixhr.py
index 13a9cd6..8a601e8 100644
--- a/lib/lp/testing/yuixhr.py
+++ b/lib/lp/testing/yuixhr.py
@@ -3,6 +3,8 @@
33
4"""Fixture code for YUITest + XHR integration testing."""4"""Fixture code for YUITest + XHR integration testing."""
55
6from __future__ import absolute_import, print_function
7
6__metaclass__ = type8__metaclass__ = type
7__all__ = [9__all__ = [
8 'login_as_person',10 'login_as_person',
@@ -146,7 +148,7 @@ class CloseDbResult:
146 yield ''148 yield ''
147 LibrarianLayer.testSetUp()149 LibrarianLayer.testSetUp()
148 except Exception:150 except Exception:
149 print "Hm, serious error when trying to clean up the test."151 print("Hm, serious error when trying to clean up the test.")
150 traceback.print_exc()152 traceback.print_exc()
151 # We're done, so we can yield the body.153 # We're done, so we can yield the body.
152 yield '\n'154 yield '\n'

Subscribers

People subscribed via source and target branches

to status/vote changes: