← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/banish-get_feedback_messages into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/banish-get_feedback_messages into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #244371 in Launchpad itself: "get_feedback_messages() callsites have highly repetitive code"
  https://bugs.launchpad.net/launchpad/+bug/244371

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/banish-get_feedback_messages/+merge/187973

Destroy usage of get_feedback_messages() in doctests, and stop importing it into the namespace that doctests use. All callsites in doctests now use print_feedback_messages().
-- 
https://code.launchpad.net/~stevenk/launchpad/banish-get_feedback_messages/+merge/187973
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/banish-get_feedback_messages into lp:launchpad.
=== modified file 'lib/lp/answers/stories/question-add.txt'
--- lib/lp/answers/stories/question-add.txt	2011-08-21 14:21:06 +0000
+++ lib/lp/answers/stories/question-add.txt	2013-09-27 04:45:05 +0000
@@ -61,8 +61,7 @@
     >>> user_browser.getControl('Summary').value
     ''
     >>> user_browser.getControl('Continue').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     You must enter a summary of your problem.
 
@@ -139,8 +138,7 @@
 If the user doesn't provide details, he'll get an error message:
 
     >>> user_browser.getControl('Post Question').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     You must provide details about your problem.
 
@@ -149,8 +147,7 @@
 
     >>> user_browser.getControl('Summary').value = ''
     >>> user_browser.getControl('Post Question').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There are 2 errors.
     You must enter a summary of your problem.
 

=== modified file 'lib/lp/answers/stories/question-subscriptions.txt'
--- lib/lp/answers/stories/question-subscriptions.txt	2011-08-02 02:51:59 +0000
+++ lib/lp/answers/stories/question-subscriptions.txt	2013-09-27 04:45:05 +0000
@@ -23,8 +23,7 @@
 
 A message confirming that he was subscribed is displayed:
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     You have subscribed to this question.
 
 
@@ -46,8 +45,7 @@
 
 A confirmation is displayed:
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     You have unsubscribed from this question.
 
 
@@ -67,7 +65,6 @@
 
 A notification message is displayed notifying of the subscription:
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Thanks for your information request.
     You have subscribed to this question.

=== modified file 'lib/lp/answers/stories/this-is-a-faq.txt'
--- lib/lp/answers/stories/this-is-a-faq.txt	2012-12-11 05:41:50 +0000
+++ lib/lp/answers/stories/this-is-a-faq.txt	2013-09-27 04:45:05 +0000
@@ -171,8 +171,7 @@
 
     >>> print user_browser.url
     http://answers.launchpad.dev/firefox/+question/2/+linkfaq
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     You didn't modify the linked FAQ.
 

=== modified file 'lib/lp/app/stories/launchpad-root/site-search.txt'
--- lib/lp/app/stories/launchpad-root/site-search.txt	2012-12-26 07:23:19 +0000
+++ lib/lp/app/stories/launchpad-root/site-search.txt	2013-09-27 04:45:05 +0000
@@ -292,8 +292,7 @@
 
     >>> too_many_characters = '12345 7890' * 25 + 'n'
     >>> search_for(too_many_characters)
-    >>> for message in get_feedback_messages(anon_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(anon_browser.contents)
     There is 1 error.
     The search text cannot exceed 250 characters.
 

=== modified file 'lib/lp/blueprints/stories/standalone/xx-branch-links.txt'
--- lib/lp/blueprints/stories/standalone/xx-branch-links.txt	2012-08-07 07:07:02 +0000
+++ lib/lp/blueprints/stories/standalone/xx-branch-links.txt	2013-09-27 04:45:05 +0000
@@ -85,8 +85,7 @@
     >>> browser.getLink('Link to another branch').click()
     >>> browser.getControl('Branch').value = '~mark/firefox/release-0.8'
     >>> browser.getControl('Continue').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     This branch has already been linked to the blueprint
 

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt	2012-02-21 22:30:41 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-also-affects-new-upstream.txt	2013-09-27 04:45:05 +0000
@@ -42,7 +42,7 @@
     >>> print user_browser.title
     Register project affected by...
 
-    >>> print "\n".join(get_feedback_messages(user_browser.contents))
+    >>> print_feedback_messages(user_browser.contents)
     There are some projects using the bug tracker you specified. One of
     these may be the one you were trying to register.
     >>> control = user_browser.getControl(name='field.existing_product')
@@ -75,7 +75,9 @@
     >>> user_browser.getControl('Use Existing Project').click()
     >>> print user_browser.title
     Register project affected by...
-    >>> print get_feedback_messages(user_browser.contents)[-1]
+    >>> print_feedback_messages(user_browser.contents)
+    There is 1 error.
+    ...
     A fix for this bug has already been requested for The Foo Project
 
 Now we'll tell Launchpad to not use the existing upstream as we want to report
@@ -106,7 +108,7 @@
     >>> user_browser.getControl('Continue').click()
     >>> print user_browser.title
     Register project affected by...
-    >>> print "\n".join(get_feedback_messages(user_browser.contents))
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     Launchpad does not recognize the bug tracker at this URL.
 

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt	2012-10-09 10:28:02 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt	2013-09-27 04:45:05 +0000
@@ -34,10 +34,10 @@
     >>> browser.url
     'http://bugs.launchpad.dev/ubuntu/+bug/6/+distrotask'
 
-    >>> print get_feedback_messages(browser.contents)
-    [u'There is 1 error.',
-     u'This bug is already on Ubuntu. Please specify an affected package
-       in which the bug has not yet been reported.']
+    >>> print_feedback_messages(browser.contents)
+    There is 1 error.
+    This bug is already on Ubuntu. Please specify an affected package
+    in which the bug has not yet been reported.
 
 You also can't add another task on a distro source package, when there's
 already a distro task open. (Instead, you should target the existing
@@ -48,10 +48,10 @@
     >>> browser.getControl("Distribution").value = ["ubuntu"]
     >>> browser.getControl("Source Package Name").value = "mozilla-firefox"
     >>> browser.getControl("Continue").click()
-    >>> print get_feedback_messages(browser.contents)
-    [u'There is 1 error.',
-     u'This bug is already open on Ubuntu with no package specified.
-       You should fill in a package name for the existing bug.']
+    >>> print_feedback_messages(browser.contents)
+    There is 1 error.
+    This bug is already open on Ubuntu with no package specified.
+    You should fill in a package name for the existing bug.
 
 Let's assign the existing Ubuntu task to mozilla-firefox, then add
 another task on Ubuntu evolution.
@@ -78,10 +78,9 @@
     >>> browser.getControl(name="ubuntu_mozilla-firefox.target.package"
     ...     ).value = "evolution"
     >>> browser.getControl("Save Changes").click()
-    >>> print get_feedback_messages(browser.contents)
-    [...There is 1 error in the data you entered...
-     u'A fix for this bug has already been requested for evolution in
-       Ubuntu']
+    >>> print_feedback_messages(browser.contents)
+    There is 1 error in the data you entered...
+    A fix for this bug has already been requested for evolution in Ubuntu
 
 Now let's add a Debian task to bug 1. Since Debian doesn't use
 Launchpad, we add a bug watch as well.
@@ -111,12 +110,12 @@
     >>> print browser.url
     http://bugs.launchpad.dev/debian/+source/alsa-utils/+bug/1/+distrotask
 
-    >>> print get_feedback_messages(browser.contents)
-    [u'There is 1 error.',
-     u'Bug watches can not be added for Ubuntu, as it uses Launchpad as
-       its official bug tracker. Alternatives are to add a watch for
-       another project, or a comment containing a URL to the related
-       bug report.']
+    >>> print_feedback_messages(browser.contents)
+    There is 1 error.
+    Bug watches can not be added for Ubuntu, as it uses Launchpad as
+    its official bug tracker. Alternatives are to add a watch for
+    another project, or a comment containing a URL to the related
+    bug report.
 
 If we remove the remote bug it will work.
 
@@ -138,10 +137,9 @@
     >>> print browser.url
     http://bugs.../ubuntu/+source/mozilla-firefox/+bug/1/+editstatus
 
-    >>> print get_feedback_messages(browser.contents)
-    [...There is 1 error in the data you entered...
-     u'A fix for this bug has already been requested for alsa-utils in
-       Ubuntu']
+    >>> print_feedback_messages(browser.contents)
+    There is 1 error in the data you entered...
+    A fix for this bug has already been requested for alsa-utils in Ubuntu
 
     >>> browser.getControl(name="ubuntu_mozilla-firefox.target.package"
     ...     ).value = 'pmount'
@@ -170,8 +168,8 @@
     >>> print browser.url
     http://bugs.launchpad.dev/ubuntu/+source/pmount/+bug/1/+distrotask
 
-    >>> print get_feedback_messages(browser.contents)
-    [u"Debian doesn't use Launchpad as its bug tracker. ...]
+    >>> print_feedback_messages(browser.contents)
+    Debian doesn't use Launchpad as its bug tracker. ...
 
 The form is shown as well, so it's possible to easily change the field
 values, in order to add a bug watch.
@@ -186,8 +184,8 @@
     >>> print browser.url
     http://bugs.launchpad.dev/ubuntu/+source/pmount/+bug/1/+distrotask
 
-    >>> print get_feedback_messages(browser.contents)
-    [u"Debian doesn't use Launchpad as its bug tracker. ...]
+    >>> print_feedback_messages(browser.contents)
+    Debian doesn't use Launchpad as its bug tracker. ...
 
 If we confirm that we indeed want to add an unlinked task, we get
 redirected to the bug page.
@@ -235,10 +233,10 @@
     >>> print browser.url
     http://bugs.launchpad.dev/proprietary-product/+bug/16/+choose-affected-product
 
-    >>> print get_feedback_messages(browser.contents)
-    [u'There is 1 error.',
-     u'This proprietary bug already affects Proprietary Product.
-       Proprietary bugs cannot affect multiple projects.']
+    >>> print_feedback_messages(browser.contents)
+    There is 1 error.
+    This proprietary bug already affects Proprietary Product.
+    Proprietary bugs cannot affect multiple projects.
 
 
 Forwarding bugs upstream
@@ -350,8 +348,9 @@
 
 We get a nice error message.
 
-    >>> print get_feedback_messages(user_browser.contents)
-    [u'There is 1 error.', u'Required input is missing.']
+    >>> print_feedback_messages(user_browser.contents)
+    There is 1 error.
+    Required input is missing.
 
 If we enter a product name that doesn't exist, we inform the user about
 this and ask him to search for the product.
@@ -361,9 +360,9 @@
     >>> print user_browser.url
     http://.../debian/+source/mozilla-firefox/+bug/3/+choose-affected-product
 
-    >>> print get_feedback_messages(user_browser.contents)
-    [u'There is 1 error.',
-     u'There is no project in Launchpad named "no-such-product"...]
+    >>> print_feedback_messages(user_browser.contents)
+    There is 1 error.
+    There is no project in Launchpad named "no-such-product"...
 
     >>> search_link = user_browser.getLink('search for it')
     >>> print search_link.url
@@ -381,9 +380,9 @@
     >>> print user_browser.url
     http://.../debian/+source/mozilla-firefox/+bug/3/+choose-affected-product
 
-    >>> print get_feedback_messages(user_browser.contents)
-    [u'There is 1 error.',
-     u'There is no project in Launchpad named "N\xf6 Such Product&&lt...]
+    >>> print_feedback_messages(user_browser.contents)
+    There is 1 error.
+    There is no project in Launchpad named "N... Such Product&&lt...
 
 
 Linking to bug watches
@@ -438,9 +437,9 @@
 
 We get a nice error message.
 
-    >>> print get_feedback_messages(user_browser.contents)
-    [u'There is 1 error.',
-     u'A fix for this bug has already been requested for alsa-utils']
+    >>> print_feedback_messages(user_browser.contents)
+    There is 1 error.
+    A fix for this bug has already been requested for alsa-utils
 
 We can add another upstream to the bug.
 
@@ -463,9 +462,9 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/evolution/+bug/3/+editstatus
 
-    >>> print get_feedback_messages(user_browser.contents)
-     [u'There is 1 error in the data you entered...
-      u'A fix for this bug has already been requested for alsa-utils']
+    >>> print_feedback_messages(user_browser.contents)
+    There is 1 error in the data you entered...
+    A fix for this bug has already been requested for alsa-utils
 
 
 Adding bugtask with bug watch
@@ -569,7 +568,8 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/firefox/+bug/4/+choose-affected-product
 
-    >>> print get_feedback_messages(user_browser.contents)[0]
+    >>> print_feedback_messages(user_browser.contents)
+    ...
     The bug tracker with the given URL is not registered in Launchpad.
     Would you like to register it now?
 

=== modified file 'lib/lp/bugs/stories/bug-also-affects/xx-duplicate-bugwatches.txt'
--- lib/lp/bugs/stories/bug-also-affects/xx-duplicate-bugwatches.txt	2012-07-10 08:12:53 +0000
+++ lib/lp/bugs/stories/bug-also-affects/xx-duplicate-bugwatches.txt	2013-09-27 04:45:05 +0000
@@ -77,8 +77,7 @@
     ...     'mozilla-firefox')
     >>> user_browser.getControl('URL').value = debian_bug
     >>> user_browser.getControl('Continue').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...   print message
+    >>> print_feedback_messages(user_browser.contents)
     Bug #8 also links to the added bug watch (debbugs #42).
 
 The notification links to the bug in question.

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-comment-attach-file.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-comment-attach-file.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-comment-attach-file.txt	2013-09-27 04:45:05 +0000
@@ -20,8 +20,8 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/firefox/+bug/1
 
-    >>> get_feedback_messages(user_browser.contents)
-    [u'Thank you for your comment.']
+    >>> print_feedback_messages(user_browser.contents)
+    Thank you for your comment.
 
 When an attachment is submitted, the comment and description are optional.
 
@@ -33,8 +33,8 @@
     ...     StringIO("a test file"), "text/plain", "foo.txt")
     >>> user_browser.getControl("Post Comment").click()
 
-    >>> get_feedback_messages(user_browser.contents)
-    [u'Attachment foo.txt added to bug.']
+    >>> print_feedback_messages(user_browser.contents)
+    Attachment foo.txt added to bug.
 
 A comment and attachment can be submitted in one request.
 
@@ -50,8 +50,9 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/firefox/+bug/1
 
-    >>> get_feedback_messages(user_browser.contents)
-    [u'Thank you for your comment.', u'Attachment bar.txt added to bug.']
+    >>> print_feedback_messages(user_browser.contents)
+    Thank you for your comment.
+    Attachment bar.txt added to bug.
 
 You cannot upload an empty attachment.
 
@@ -64,8 +65,7 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/firefox/+bug/1/+addcomment
 
-    >>> for msg in get_feedback_messages(user_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     Cannot upload empty file.
 
@@ -83,8 +83,7 @@
     ...     StringIO("x"*1025), "text/plain", "foo.txt")
     >>> user_browser.getControl("Post Comment").click()
 
-    >>> for msg in get_feedback_messages(user_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     Cannot upload files larger than 1024 bytes
 
@@ -100,5 +99,5 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/firefox/+bug/1
 
-    >>> get_feedback_messages(user_browser.contents)
-    [u'Attachment foo.txt added to bug.']
+    >>> print_feedback_messages(user_browser.contents)
+    Attachment foo.txt added to bug.

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-create-question.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-create-question.txt	2013-04-11 02:12:09 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-create-question.txt	2013-09-27 04:45:05 +0000
@@ -70,8 +70,7 @@
     >>> user_browser.open(
     ...     'http://bugs.launchpad.dev'
     ...     '/ubuntu/+source/linux-source-2.6.15/+bug/10/+editstatus')
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     This bug was converted into a question. It cannot be edited.
 
     >>> user_browser.getControl('Save Changes')
@@ -238,8 +237,9 @@
     >>> user_browser.title
     'Bug #12 ... : Bugs : Jokosher'
 
-    >>> print "\n".join(get_feedback_messages(user_browser.contents))
-    Removed Question #...: Copy, Cut and Delete operations should work...
+    >>> print_feedback_messages(user_browser.contents)
+    Removed Question #...:
+    Copy, Cut and Delete operations should work...
 
     >>> portlet = find_portlet(user_browser.contents, 'Related questions')
     >>> print portlet

=== modified file 'lib/lp/bugs/stories/bugtask-searches/xx-advanced-people-filters.txt'
--- lib/lp/bugs/stories/bugtask-searches/xx-advanced-people-filters.txt	2012-12-26 07:23:19 +0000
+++ lib/lp/bugs/stories/bugtask-searches/xx-advanced-people-filters.txt	2013-09-27 04:45:05 +0000
@@ -56,16 +56,16 @@
     >>> anon_browser.getControl(name='field.assignee').value = (
     ...     'invalid-assignee')
     >>> anon_browser.getControl('Search', index=0).click()
-    >>> get_feedback_messages(anon_browser.contents)
-    [u'There's no person with the name or email address 'invalid-assignee'.']
+    >>> print_feedback_messages(anon_browser.contents)
+    There's no person with the name or email address 'invalid-assignee'.
 
     >>> anon_browser.open(
     ...     'http://bugs.launchpad.dev/~name12/+reportedbugs?advanced=1')
     >>> anon_browser.getControl(name='field.assignee').value = (
     ...     'invalid-assignee')
     >>> anon_browser.getControl('Search', index=0).click()
-    >>> get_feedback_messages(anon_browser.contents)
-    [u'There's no person with the name or email address 'invalid-assignee'.']
+    >>> print_feedback_messages(anon_browser.contents)
+    There's no person with the name or email address 'invalid-assignee'.
 
 
 Searching by reporter
@@ -93,16 +93,16 @@
     >>> anon_browser.getControl(name='field.bug_reporter').value = (
     ...     'invalid-reporter')
     >>> anon_browser.getControl('Search', index=0).click()
-    >>> get_feedback_messages(anon_browser.contents)
-    [u'There's no person with the name or email address 'invalid-reporter'.']
+    >>> print_feedback_messages(anon_browser.contents)
+    There's no person with the name or email address 'invalid-reporter'.
 
     >>> anon_browser.open('http://bugs.launchpad.dev/~name12/+assignedbugs')
     >>> anon_browser.getControl(name='field.bug_reporter').value = (
     ...     'invalid-reporter')
     >>> anon_browser.getControl('Search', index=0).click()
-    >>> get_feedback_messages(anon_browser.contents)
-    [u'There's no person with the name or email address
-    'invalid-reporter'.']
+    >>> print_feedback_messages(anon_browser.contents)
+    There's no person with the name or email address
+    'invalid-reporter'.
 
 
 Searching for a bug commenter's bugs
@@ -208,8 +208,7 @@
 
     >>> browser.getControl('Further information').value = 'Test Bug 1'
     >>> browser.getControl('Submit').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Thank you for your bug report...
 
     >>> bug_1_url = browser.url
@@ -219,8 +218,7 @@
     >>> browser.getControl('Continue').click()
     >>> browser.getControl('Further information').value = 'Test Bug 2'
     >>> browser.getControl('Submit').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Thank you for your bug report...
 
     >>> bug_2_url = browser.url
@@ -234,8 +232,7 @@
 
     >>> browser.getControl('Person').value = subscriber
     >>> browser.getControl('Subscribe user').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     No Privileges Person has been subscribed to this bug...
 
 Now if we repeat our earlier search for bugs our user is subscribed to,
@@ -258,8 +255,7 @@
 
     >>> browser.getControl('Person').value = subscriber
     >>> browser.getControl('Subscribe user').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     No Privileges Person has been subscribed to this bug...
 
 Finally, if we repeat our earlier search for bugs our user is subscribed

=== modified file 'lib/lp/bugs/stories/bugtracker/xx-bugtracker.txt'
--- lib/lp/bugs/stories/bugtracker/xx-bugtracker.txt	2013-05-09 08:53:01 +0000
+++ lib/lp/bugs/stories/bugtracker/xx-bugtracker.txt	2013-09-27 04:45:05 +0000
@@ -217,8 +217,7 @@
     >>> user_browser.url
     'http://bugs.launchpad.dev/bugs/bugtrackers/testmantis/+edit'
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     http://bugzilla.mozilla.org/ is already registered in Launchpad
     as "The Mozilla.org Bug Tracker" (mozilla.org).
@@ -230,8 +229,7 @@
     ...     'what? my wife does this stuff')
     >>> user_browser.getControl('Change').click()
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     "what? my wife does this stuff" is not a valid URI
 
@@ -239,8 +237,7 @@
     ...     'http://ξνεr.been.fishing?')
     >>> user_browser.getControl('Change').click()
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     URIs must consist of ASCII characters
 
@@ -323,8 +320,7 @@
     ...     'http://bugzilla.mozilla.org/')
     >>> user_browser.getControl('Change').click()
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     http://bugzilla.mozilla.org/ is already registered in Launchpad
     as "The Mozilla.org Bug Tracker" (mozilla.org).
@@ -356,8 +352,7 @@
     ...     'ξνεr been http://fishing?')
     >>> user_browser.getControl('Change').click()
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     URIs must consist of ASCII characters
     "been" is not a valid URI
@@ -389,8 +384,7 @@
     >>> user_browser.url
     'http://bugs.launchpad.dev/bugs/bugtrackers'
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Freddy's Bugs has been deleted.
 
 Bug trackers can be deleted by anyone, subject to a few restrictions:
@@ -513,8 +507,7 @@
 disabled.
 
     >>> user_browser.open('http://launchpad.dev/bugs/15')
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Bug watch updates for Debian Bug tracker are disabled.
 
 Inactive bug trackers are displayed in a separate table from the active
@@ -548,8 +541,7 @@
 The message won't appear on the bug pages either.
 
     >>> user_browser.open('http://launchpad.dev/bugs/15')
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
 
 And the inactive bug trackers table will have disappeared since there
 are no inactive bug trackers.

=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-distro-guided-filebug.txt'
--- lib/lp/bugs/stories/guided-filebug/xx-distro-guided-filebug.txt	2011-04-20 14:56:23 +0000
+++ lib/lp/bugs/stories/guided-filebug/xx-distro-guided-filebug.txt	2013-09-27 04:45:05 +0000
@@ -31,8 +31,7 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/thunderbird/+bug/9
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     This bug has been marked as affecting you.
 
 Actually, on reflection, that's not our bug after all. Let's go

=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-filebug-attachments.txt'
--- lib/lp/bugs/stories/guided-filebug/xx-filebug-attachments.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/bugs/stories/guided-filebug/xx-filebug-attachments.txt	2013-09-27 04:45:05 +0000
@@ -35,8 +35,7 @@
 No Privileges Person sees a notice on the bug page stating that the file
 was attached.
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Thank you for your bug report.
     The file "example.txt" was attached to the bug report.
 

=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt'
--- lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt	2012-08-02 07:02:18 +0000
+++ lib/lp/bugs/stories/guided-filebug/xx-product-guided-filebug.txt	2013-09-27 04:45:05 +0000
@@ -107,8 +107,7 @@
     >>> print user_browser.url
     http://bugs.launchpad.dev/firefox/+bug/1
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     This bug has been marked as affecting you.
 
 It's also possible to subscribe to the suggested duplicates. This is
@@ -128,8 +127,7 @@
     >>> user_browser.getControl(
     ...     "Yes, this is the bug I'm trying to report").click()
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     This bug is already marked as affecting you.
     You have subscribed to this bug report.
 

=== modified file 'lib/lp/code/stories/branches/xx-branch-deletion.txt'
--- lib/lp/code/stories/branches/xx-branch-deletion.txt	2012-04-10 14:01:17 +0000
+++ lib/lp/code/stories/branches/xx-branch-deletion.txt	2013-09-27 04:45:05 +0000
@@ -49,8 +49,7 @@
     >>> browser.getControl('Delete').click()
     >>> print browser.url
     http://code.launchpad.dev/earthlynx
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Branch ~alice/earthlynx/to-delete deleted...
 
 If the branch is junk, then the user is taken back to the code listing for
@@ -65,8 +64,7 @@
     >>> browser.getControl('Delete').click()
     >>> print browser.url
     http://code.launchpad.dev/~alice
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Branch ~alice/+junk/to-delete deleted...
 
 Branches that are stacked upon cannot be deleted.
@@ -105,6 +103,5 @@
     >>> browser.open(branch_url)
     >>> browser.getLink('Delete branch').click()
     >>> browser.getControl('Delete').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Branch ... deleted...

=== modified file 'lib/lp/code/stories/branches/xx-branch-edit.txt'
--- lib/lp/code/stories/branches/xx-branch-edit.txt	2012-06-16 13:27:45 +0000
+++ lib/lp/code/stories/branches/xx-branch-edit.txt	2013-09-27 04:45:05 +0000
@@ -175,8 +175,7 @@
     >>> browser.url
     'http://code.launchpad.dev/%7Ename12/gnome-terminal/main/+edit'
 
-    >>> for msg in get_feedback_messages(browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     You already have a branch for GNOME Terminal called 2.6.
 
@@ -192,8 +191,7 @@
     ...     'http://bazaar.launchpad.dev/~foo/bar/baz')
     >>> browser.getControl('Change Branch').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     For Launchpad to mirror a branch, the original branch cannot be
     on launchpad.dev.
@@ -298,8 +296,7 @@
 
 When the owner is changed a notification is shown.
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     The branch owner has been changed to Landscape Developers ...
 
 

=== modified file 'lib/lp/code/stories/branches/xx-bug-branch-links.txt'
--- lib/lp/code/stories/branches/xx-bug-branch-links.txt	2012-08-07 07:07:02 +0000
+++ lib/lp/code/stories/branches/xx-bug-branch-links.txt	2013-09-27 04:45:05 +0000
@@ -65,8 +65,7 @@
     >>> browser.getLink('Link a bug report').click()
     >>> browser.getControl('Bug #').value = "4"
     >>> browser.getControl('Continue').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
 
 The bug-branch link is also shown on the bug page.
 

=== modified file 'lib/lp/code/stories/branches/xx-subscribing-branches.txt'
--- lib/lp/code/stories/branches/xx-subscribing-branches.txt	2013-04-11 02:12:09 +0000
+++ lib/lp/code/stories/branches/xx-subscribing-branches.txt	2013-09-27 04:45:05 +0000
@@ -166,8 +166,7 @@
     ...     '5000 lines']
     >>> browser.getControl('Subscribe').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     Required input is missing.
 

=== modified file 'lib/lp/code/stories/branches/xx-upgrading-branches.txt'
--- lib/lp/code/stories/branches/xx-upgrading-branches.txt	2012-01-15 13:32:27 +0000
+++ lib/lp/code/stories/branches/xx-upgrading-branches.txt	2013-09-27 04:45:05 +0000
@@ -43,6 +43,5 @@
     http://code.launchpad.dev/~domino/.../+upgrade
     >>> domino_browser.getControl('Upgrade').click()
 
-    >>> for msg in get_feedback_messages(domino_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(domino_browser.contents)
     An upgrade of this branch is in progress.

=== modified file 'lib/lp/code/stories/codeimport/xx-admin-codeimport.txt'
--- lib/lp/code/stories/codeimport/xx-admin-codeimport.txt	2012-10-09 01:07:52 +0000
+++ lib/lp/code/stories/codeimport/xx-admin-codeimport.txt	2013-09-27 04:45:05 +0000
@@ -124,8 +124,7 @@
     >>> import_browser.getControl('URL').value = \
     ...     'svn://user:password@xxxxxxxxxxxxxxxxxxx/fooix/trunk'
     >>> import_browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been updated.
 
 bzr-svn imports,
@@ -134,8 +133,7 @@
     >>> import_browser.getControl('URL').value = \
     ...     'svn://user:password@xxxxxxxxxxxxxxxxxxx/bzr-svn/trunk'
     >>> import_browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been updated.
 
 CVS imports,
@@ -146,8 +144,7 @@
     >>> import_browser.getControl('Module').value = \
     ...     'fooix2'
     >>> import_browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been updated.
 
 Git imports,
@@ -156,8 +153,7 @@
     >>> import_browser.getControl('URL').value = \
     ...     'git://user:password@xxxxxxxxxxxxxxxxxxx/fooix'
     >>> import_browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been updated.
 
 and imports targetting source packages.
@@ -166,8 +162,7 @@
     >>> import_browser.getControl('URL').value = \
     ...     'http://metal.example.org/zap'
     >>> import_browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been updated.
 
 
@@ -179,8 +174,7 @@
     >>> print_import_details(import_browser)
     Import Status: Invalid
     ...
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been set as invalid.
 
 
@@ -192,8 +186,7 @@
     >>> print_import_details(import_browser)
     Import Status: Suspended
     ...
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been suspended.
 
 
@@ -205,8 +198,7 @@
     >>> print_import_details(import_browser)
     Import Status: Failed
     ...
-    >>> for message in get_feedback_messages(import_browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(import_browser.contents)
     The code import has been marked as failing.
 
 
@@ -307,8 +299,7 @@
 displayed.
 
     >>> sample_person_browser.getControl('Import Now').click()
-    >>> for msg in get_feedback_messages(sample_person_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(sample_person_browser.contents)
     Import will run as soon as possible.
     >>> print_import_details(sample_person_browser)
     Import Status: Reviewed

=== modified file 'lib/lp/code/stories/codeimport/xx-create-codeimport.txt'
--- lib/lp/code/stories/codeimport/xx-create-codeimport.txt	2012-11-07 05:06:18 +0000
+++ lib/lp/code/stories/codeimport/xx-create-codeimport.txt	2013-09-27 04:45:05 +0000
@@ -106,8 +106,7 @@
     ...     "http://bazaar.launchpad.net/firefox/trunk";)
     >>> browser.getControl('Project').value = "firefox"
     >>> browser.getControl('Request Import').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     You can not create imports for branches that are hosted by Launchpad.
 
@@ -224,8 +223,7 @@
     >>> browser.getControl('Module').value = "firefox"
     >>> browser.getControl('Request Import').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     CVS root is invalid.
 
@@ -248,8 +246,7 @@
     >>> browser.getControl('Module').value = "firefox"
     >>> browser.getControl('Request Import').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     Those CVS details are already specified for
     the imported branch ~no-priv/firefox/import2.
@@ -259,8 +256,7 @@
     ...     "http://svn.example.com/firefox/trunk";)
     >>> browser.getControl('Request Import').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     This foreign branch URL is already specified for
     the imported branch ~no-priv/firefox/imported.
@@ -281,8 +277,7 @@
     >>> browser.getControl('Branch URL', index=1).value = (
     ...     "http://svn.example.com/firefox/other";)
     >>> browser.getControl('Request Import').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     There is already an existing import for firefox with the name of imported.
 
@@ -299,8 +294,7 @@
     >>> browser.getControl('Branch URL', index=0).value = (
     ...     "http://svn.example.com/launchpage/fake";)
     >>> browser.getControl('Request Import').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     You are not allowed to register imports for Launchpad.
 
@@ -317,8 +311,7 @@
     >>> browser.getControl('Branch URL', index=0).value = (
     ...     "http://svn.example.com/launchpage/fake";)
     >>> browser.getControl('Request Import').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     Invalid value
 

=== modified file 'lib/lp/code/stories/codeimport/xx-edit-codeimport.txt'
--- lib/lp/code/stories/codeimport/xx-edit-codeimport.txt	2012-01-15 13:32:27 +0000
+++ lib/lp/code/stories/codeimport/xx-edit-codeimport.txt	2013-09-27 04:45:05 +0000
@@ -136,8 +136,7 @@
 displayed.
 
     >>> sample_person_browser.getControl('Import Now').click()
-    >>> for msg in get_feedback_messages(sample_person_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(sample_person_browser.contents)
     Import will run as soon as possible.
     >>> print_import_details(sample_person_browser)
     Import Status: Reviewed

=== modified file 'lib/lp/coop/answersbugs/stories/question-buglink.txt'
--- lib/lp/coop/answersbugs/stories/question-buglink.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/coop/answersbugs/stories/question-buglink.txt	2013-09-27 04:45:05 +0000
@@ -58,8 +58,7 @@
 
 A notification is also displayed.
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Added link to bug #1: ...Firefox does not support SVG...
 
 
@@ -151,8 +150,7 @@
     >>> browser.open('http://launchpad.dev/firefox/+question/2')
     >>> browser.getLink('Remove bug link').click()
     >>> soup = find_main_content(browser.contents)
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There are no links that you are allowed to remove.
 
     >>> admin_browser.getLink('Remove bug link').click()

=== modified file 'lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt'
--- lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/registry/stories/gpg-coc/xx-gpg-coc.txt	2013-09-27 04:45:05 +0000
@@ -41,8 +41,7 @@
     >>> key = "A419AE861E88BC9E04B9C26FBA2B9389DFD20543"
     >>> browser.getControl(name='fingerprint').value = key
     >>> browser.getControl(name='import').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     A message has been sent to test@xxxxxxxxxxxxx, encrypted
     with the key 1024D/DFD20543.
     To confirm the key is yours, decrypt the message and follow the
@@ -130,8 +129,7 @@
 
     >>> browser.url
     'http://launchpad.dev/~name12'
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     The key 1024D/DFD20543 was successfully validated.
 
 Certify the key is imported:
@@ -165,8 +163,7 @@
     >>> fingerprint = "447DBF38C4F9C4ED752246B77D88913717B05A8F"
     >>> browser.getControl(name='fingerprint').value = fingerprint
     >>> browser.getControl(name='import').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     A message has been sent to test@xxxxxxxxxxxxx. To
     confirm the key 1024D/17B05A8F is yours, follow
     the link inside.
@@ -239,8 +236,7 @@
     >>> browser.getControl('Continue').click()
     >>> browser.title
     'Confirm sign-only OpenPGP key'
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     Required input is missing.
 
@@ -255,8 +251,7 @@
 
     >>> browser.getControl('Signed text').value = bad
     >>> browser.getControl('Continue').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     The signed content does not match the message found in the email.
 
@@ -279,8 +274,7 @@
     ... """
     >>> browser.getControl('Signed text').value = signed_content
     >>> browser.getControl('Continue').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     The key used to sign the content (A419AE861E88BC9E04B9C26FBA2B9389DFD20543)
     is not the key you were registering
@@ -297,8 +291,7 @@
     >>> browser.getControl('Continue').click()
     >>> browser.url
     'http://launchpad.dev/~name12'
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     The key 1024D/17B05A8F was successfully validated.
 
 Now that the key has been validated, the login token is consumed:
@@ -320,8 +313,7 @@
     >>> browser.getControl(name="DEACTIVATE_GPGKEY").value = ['3']
     >>> browser.getControl('Deactivate Key').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Deactivated key(s): 1024D/17B05A8F
 
 Coming to his senses, he asks for a re-validation of the key.
@@ -329,8 +321,7 @@
     >>> browser.getControl(name="REACTIVATE_GPGKEY").value = ['3']
     >>> browser.getControl('Reactivate Key').click()
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     A message has been sent to test@xxxxxxxxxxxxx with instructions
     to reactivate these key(s): 1024D/17B05A8F
 

=== modified file 'lib/lp/registry/stories/mailinglists/hosted-email-address.txt'
--- lib/lp/registry/stories/mailinglists/hosted-email-address.txt	2011-12-22 05:09:10 +0000
+++ lib/lp/registry/stories/mailinglists/hosted-email-address.txt	2013-09-27 04:45:05 +0000
@@ -40,8 +40,7 @@
     >>> user_browser.getControl('Change').click()
     >>> print user_browser.title
     Aardvarks in Launchpad
-    >>> for msg in get_feedback_messages(user_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     A confirmation message has been sent to...
     >>> from_addr, to_addrs, raw_msg = stub.test_emails.pop()
     >>> stub.test_emails
@@ -65,8 +64,7 @@
     >>> user_browser.getControl('Continue').click()
     >>> print user_browser.title
     Aardvarks in Launchpad
-    >>> for msg in get_feedback_messages(user_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     Email address successfully confirmed.
 
     >>> user_browser.getLink(url="+contactaddress").click()

=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
--- lib/lp/registry/stories/mailinglists/lifecycle.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/mailinglists/lifecycle.txt	2013-09-27 04:45:05 +0000
@@ -29,8 +29,7 @@
     >>> browser.getControl('Create new Mailing List').click()
     >>> print browser.title
     Landscape Developers in Launchpad
-    >>> for msg in get_feedback_messages(browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(browser.contents)
     The mailing list is being created and will be available for use in a few minutes.
 
 Once the mailing list is created, the button to create it is not shown
@@ -187,8 +186,7 @@
     >>> browser.getControl('Reactivate this Mailing List').click()
     >>> mailing_list_status_message(browser.contents)
     ''
-    >>> for msg in get_feedback_messages(browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(browser.contents)
     The mailing list will be reactivated within a few minutes.
     >>> act()
     >>> transaction.commit()

=== modified file 'lib/lp/registry/stories/mailinglists/subscriptions.txt'
--- lib/lp/registry/stories/mailinglists/subscriptions.txt	2012-08-13 20:03:00 +0000
+++ lib/lp/registry/stories/mailinglists/subscriptions.txt	2013-09-27 04:45:05 +0000
@@ -126,8 +126,7 @@
     >>> admins.value = ['Preferred address']
     >>> carlos_browser.getControl('Update Subscriptions').click()
 
-    >>> for msg in get_feedback_messages(carlos_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(carlos_browser.contents)
     Subscriptions updated.
 
     >>> admins = carlos_browser.getControl(name='field.subscription.admins')
@@ -354,8 +353,8 @@
 Clicking the link will unsubscribe you from the list immediately.
 
     >>> carlos_browser.getControl('Unsubscribe').click()
-    >>> print get_feedback_messages(carlos_browser.contents)
-    [u'You have been unsubscribed from the team mailing list.']
+    >>> print_feedback_messages(carlos_browser.contents)
+    You have been unsubscribed from the team mailing list.
 
     >>> carlos_browser.open('http://launchpad.dev/~rosetta-admins')
     >>> print extract_text(
@@ -504,8 +503,8 @@
     (*) Always subscribe me to mailing lists
 
     # We only need to check this once.
-    >>> get_feedback_messages(carlos_browser.contents)
-    [u'Your auto-subscribe policy has been updated.']
+    >>> print_feedback_messages(carlos_browser.contents)
+    Your auto-subscribe policy has been updated.
 
     >>> set_autosubscribe_policy_and_submit('NEVER', carlos_browser)
     (*) Never subscribe to mailing lists

=== modified file 'lib/lp/registry/stories/milestone/object-milestones.txt'
--- lib/lp/registry/stories/milestone/object-milestones.txt	2012-10-18 15:35:05 +0000
+++ lib/lp/registry/stories/milestone/object-milestones.txt	2013-09-27 04:45:05 +0000
@@ -207,8 +207,7 @@
 
     >>> browser.getControl('Further information').value = 'Test Bug 1'
     >>> browser.getControl('Submit').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Thank you for your bug report...
 
     >>> bug_1_url = browser.url
@@ -220,8 +219,7 @@
 
     >>> browser.getControl('Further information').value = 'Test Bug 2'
     >>> browser.getControl('Submit').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Thank you for your bug report...
 
     >>> bug_2_url = browser.url

=== modified file 'lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt'
--- lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt	2012-06-11 00:03:25 +0000
+++ lib/lp/registry/stories/milestone/xx-milestone-add-and-edit.txt	2013-09-27 04:45:05 +0000
@@ -133,8 +133,7 @@
     >>> print driver_browser.title
     Series trunk : Mozilla Firefox
 
-    >>> for message in get_feedback_messages(driver_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(driver_browser.contents)
     Milestone 0.9.2 deleted.
     >>> print extract_text(find_tag_by_id(
     ...     driver_browser.contents, 'series-trunk'))

=== modified file 'lib/lp/registry/stories/object/xx-nameblacklist.txt'
--- lib/lp/registry/stories/object/xx-nameblacklist.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/registry/stories/object/xx-nameblacklist.txt	2013-09-27 04:45:05 +0000
@@ -56,7 +56,6 @@
     >>> admin_browser.getControl('Display Name').value = 'Different'
     >>> admin_browser.getControl('Save').click()
 
-    >>> print get_feedback_messages(admin_browser.contents)
-    []
+    >>> print_feedback_messages(admin_browser.contents)
     >>> "has been blocked" in admin_browser.contents
     False

=== modified file 'lib/lp/registry/stories/person/xx-add-sshkey.txt'
--- lib/lp/registry/stories/person/xx-add-sshkey.txt	2012-08-16 15:50:35 +0000
+++ lib/lp/registry/stories/person/xx-add-sshkey.txt	2013-09-27 04:45:05 +0000
@@ -183,11 +183,11 @@
     >>> browser.open('http://launchpad.dev/~salgado/+editsshkeys')
     >>> browser.getControl(name='sshkey').value = compromised_key
     >>> browser.getControl('Import Public Key').click()
-    >>> print get_feedback_messages(browser.contents)
-    [u'This key is known to be compromised due to a security flaw in the
-     software used to generate it, so it will not be accepted by Launchpad. See
-     the full Security Notice for further information and instructions on how
-     to generate another key.']
+    >>> print_feedback_messages(browser.contents)
+    This key is known to be compromised due to a security flaw in the
+    software used to generate it, so it will not be accepted by Launchpad. See
+    the full Security Notice for further information and instructions on how
+    to generate another key.
 
 
 == Keys containing non-ASCII comments ==
@@ -198,5 +198,5 @@
     >>> key = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAzSc+OzlZaURcX8NK9Hy1VoA1SXXuxFAvLw9ljz6xXEFgodmkSgSE/Pc+nR2fO/hl0rnWi//8oOwkHlwLVPQpor2cjQlceVs9rKaQcrcwRm6Jmi8CYKlEIBq82kpaLwXwK/x5ZsDfFtYUq558C5IKZOnDozthC7REPYK0cQ8gZ4bLf+5hmJ6QO4sSRZcXTZuPvgUixhlazFo6w6GqWbynf29Wp+WkLFGxGF2UE/dI8HyQy2j7ddaLnW50mGfB00B/pYtO246s84097BRUE8XoBC3SvzsZx6IbI3hOd2e834lq6kOj6QI0wu6+GINRCZf5UyNlpJv6X809XBvq68SCgw== St\xc3\xa9phane'
     >>> browser.getControl(name='sshkey').value = key
     >>> browser.getControl('Import Public Key').click()
-    >>> print get_feedback_messages(browser.contents)
-    [u'SSH public key added.']
+    >>> print_feedback_messages(browser.contents)
+    SSH public key added.

=== modified file 'lib/lp/registry/stories/person/xx-adminpeoplemerge.txt'
--- lib/lp/registry/stories/person/xx-adminpeoplemerge.txt	2011-04-08 22:39:03 +0000
+++ lib/lp/registry/stories/person/xx-adminpeoplemerge.txt	2013-09-27 04:45:05 +0000
@@ -26,7 +26,7 @@
 
     >>> admin_browser.url
     'http://launchpad.dev/people/+adminpeoplemerge'
-    >>> print get_feedback_messages(admin_browser.contents)[0]
+    >>> print_feedback_messages(admin_browser.contents)
     The following email addresses are owned by Andrew Bennetts and are going
     to be transferred to Guilherme Salgado: andrew.bennetts@xxxxxxxxxxxxxxx
 
@@ -36,6 +36,6 @@
     >>> admin_browser.url
     'http://launchpad.dev/~salgado'
 
-    >>> print get_feedback_messages(admin_browser.contents)[0]
+    >>> print_feedback_messages(admin_browser.contents)
     A merge is queued and is expected to complete in a few minutes.
 

=== modified file 'lib/lp/registry/stories/person/xx-deactivate-account.txt'
--- lib/lp/registry/stories/person/xx-deactivate-account.txt	2013-03-12 08:18:57 +0000
+++ lib/lp/registry/stories/person/xx-deactivate-account.txt	2013-09-27 04:45:05 +0000
@@ -39,8 +39,7 @@
     >>> browser.getControl('Deactivate My Account').click()
     >>> browser.url
     'http://launchpad.dev'
-    >>> for msg in get_feedback_messages(browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(browser.contents)
     Your account has been deactivated.
 
     >>> from zope.component import getUtility

=== modified file 'lib/lp/registry/stories/product/xx-product-edit-sourceforge-project.txt'
--- lib/lp/registry/stories/product/xx-product-edit-sourceforge-project.txt	2009-04-17 10:32:16 +0000
+++ lib/lp/registry/stories/product/xx-product-edit-sourceforge-project.txt	2013-09-27 04:45:05 +0000
@@ -7,8 +7,7 @@
     ...     admin_browser.getControl('Sourceforge Project').value = name
     ...     admin_browser.getControl('Change').click()
     ...     print admin_browser.url
-    ...     for message in get_feedback_messages(admin_browser.contents):
-    ...         print message
+    ...     print_feedback_messages(admin_browser.contents)
 
     >>> set_sourceforge_project('1234')
     http://launchpad.dev/firefox/+edit

=== modified file 'lib/lp/registry/stories/product/xx-product-files.txt'
--- lib/lp/registry/stories/product/xx-product-files.txt	2013-01-25 06:08:10 +0000
+++ lib/lp/registry/stories/product/xx-product-files.txt	2013-09-27 04:45:05 +0000
@@ -237,8 +237,8 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["Installer file"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'Your file 'foo.txt' has been uploaded.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    Your file 'foo.txt' has been uploaded.
 
 A file can be uploaded without a GPG signature.
 
@@ -251,8 +251,8 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["Installer file"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'Your file 'bar.txt' has been uploaded.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    Your file 'bar.txt' has been uploaded.
 
 The uploaded file is also displayed on the project's downloads page for any
 user to see.
@@ -281,8 +281,8 @@
 
     >>> checkbox_tag = table.find(attrs={'name': 'checkbox_0'})
     >>> admin_browser.getControl("Delete Files").click()
-    >>> get_feedback_messages(admin_browser.contents)
-    [u'1 file has been deleted.']
+    >>> print_feedback_messages(admin_browser.contents)
+    1 file has been deleted.
     >>> table = find_tag_by_id(admin_browser.contents, 'downloads')
     >>> for tr in table.findAll('tr'):
     ...     print extract_text(tr)
@@ -314,8 +314,7 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["Installer file"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> for msg in get_feedback_messages(firefox_owner.contents):
-    ...     print msg
+    >>> print_feedback_messages(firefox_owner.contents)
     There is 1 error.
     Required input is missing.
 
@@ -333,8 +332,7 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["Installer file"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> for msg in get_feedback_messages(firefox_owner.contents):
-    ...     print msg
+    >>> print_feedback_messages(firefox_owner.contents)
     There is 1 error.
     Cannot upload empty file.
 
@@ -349,8 +347,8 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["Installer file"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'Your file 'foo2.txt' has been uploaded.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    Your file 'foo2.txt' has been uploaded.
 
     >>> firefox_owner.open('http://launchpad.dev/firefox/1.0/1.0.0')
     >>> firefox_owner.getLink('Add download file').click()
@@ -361,8 +359,8 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["Installer file"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'Your file 'foo3.txt' has been uploaded.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    Your file 'foo3.txt' has been uploaded.
 
 Add a file to a different release on the same project.
 
@@ -375,8 +373,8 @@
     >>> firefox_owner.getControl(
     ...     name="field.contenttype").displayValue = ["README File"]
     >>> firefox_owner.getControl("Upload").click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'Your file 'foo09.txt' has been uploaded.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    Your file 'foo09.txt' has been uploaded.
 
 Examine all of the available files for download for firefox now.  They
 are listed within series in reverse chronological order, except
@@ -434,8 +432,8 @@
     ...     name="checkbox_2_1")
     >>> checkbox.value = checkbox.options
     >>> firefox_owner.getControl("Delete Files").click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'1 file has been deleted.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    1 file has been deleted.
 
 Ensure the file is no longer listed.
 
@@ -490,5 +488,5 @@
     >>> checkbox = firefox_owner.getControl(name="checkbox_0")
     >>> checkbox.value = checkbox.options
     >>> firefox_owner.getControl('Delete Files').click()
-    >>> get_feedback_messages(firefox_owner.contents)
-    [u'1 file has been deleted.']
+    >>> print_feedback_messages(firefox_owner.contents)
+    1 file has been deleted.

=== modified file 'lib/lp/registry/stories/product/xx-product-index.txt'
--- lib/lp/registry/stories/product/xx-product-index.txt	2012-12-07 19:45:34 +0000
+++ lib/lp/registry/stories/product/xx-product-index.txt	2013-09-27 04:45:05 +0000
@@ -211,8 +211,7 @@
     >>> owner_browser.getControl(name='field.voucher').value = [
     ...     'LPCBS12-f78df324-0cc2-11dd-8b6b-bac000000005']
     >>> owner_browser.getControl('Redeem').click()
-    >>> for message in get_feedback_messages(owner_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(owner_browser.contents)
     Voucher redeemed successfully
 
  The owner will now see the expiration information on the project

=== modified file 'lib/lp/registry/stories/product/xx-product-package-pages.txt'
--- lib/lp/registry/stories/product/xx-product-package-pages.txt	2010-03-03 00:40:03 +0000
+++ lib/lp/registry/stories/product/xx-product-package-pages.txt	2013-09-27 04:45:05 +0000
@@ -62,8 +62,7 @@
     >>> print evo_owner.title
     Linked packages...
 
-    >>> for message in get_feedback_messages(evo_owner.contents):
-    ...     print message
+    >>> print_feedback_messages(evo_owner.contents)
     Removed upstream association between Evolution trunk series and Warty.
 
     >>> print extract_text(find_tag_by_id(

=== modified file 'lib/lp/registry/stories/productrelease/xx-productrelease-basics.txt'
--- lib/lp/registry/stories/productrelease/xx-productrelease-basics.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/productrelease/xx-productrelease-basics.txt	2013-09-27 04:45:05 +0000
@@ -63,8 +63,8 @@
 
     >>> browser.open(
     ...     'http://launchpad.dev/firefox/+milestone/1.0/+addrelease')
-    >>> print get_feedback_messages(browser.contents)
-    [u'A project release already exists for this milestone.']
+    >>> print_feedback_messages(browser.contents)
+    A project release already exists for this milestone.
 
 The milestone deactivation notice is not shown when the user selects the
 the "Keep the milestone <name> active" checkbox.
@@ -76,11 +76,9 @@
     >>> browser.getControl("Date released").value = '2009-01-01'
     >>> browser.getControl("Keep the bar milestone active.").selected = True
     >>> browser.getControl('Create release').click()
-    >>> print get_feedback_messages(browser.contents)
-    []
+    >>> print_feedback_messages(browser.contents)
     >>> browser.open('http://launchpad.dev/firefox/trunk/bar')
-    >>> print get_feedback_messages(browser.contents)
-    []
+    >>> print_feedback_messages(browser.contents)
 
 
 == Checking releases with the same version ==

=== modified file 'lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt'
--- lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt	2013-09-27 04:45:05 +0000
@@ -55,8 +55,7 @@
     >>> print salgados_browser.title
     Series trunk : Mozilla Firefox
 
-    >>> for message in get_feedback_messages(salgados_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(salgados_browser.contents)
     Release 0.9.2 deleted.
 
 Milestone 0.9.2 no longer has a release. The release column explains that

=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-delete.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-delete.txt	2010-09-24 14:40:08 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-delete.txt	2013-09-27 04:45:05 +0000
@@ -88,8 +88,7 @@
     >>> print owner_browser.title
     Mozilla Firefox in Launchpad
 
-    >>> for message in get_feedback_messages(owner_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(owner_browser.contents)
     Series trunk deleted.
 
     >>> owner_browser.getLink('trunk')

=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-set-branch.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-set-branch.txt	2012-10-09 01:07:52 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-set-branch.txt	2013-09-27 04:45:05 +0000
@@ -38,8 +38,7 @@
 
     >>> browser.getControl(name='field.branch_location').value = branch_name
     >>> browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Series code location updated.
     >>> print browser.url
     http://launchpad.dev/firefox/trunk
@@ -61,8 +60,7 @@
     >>> browser.getControl('Branch URL').value = (
     ...     'https://bzr.example.com/branch')
     >>> browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Series code location updated.
     >>> print browser.url
     http://launchpad.dev/firefox/trunk
@@ -77,8 +75,7 @@
     >>> browser.getControl('Branch URL').value = (
     ...     'git://git.example.com/branch')
     >>> browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Code import created and branch linked to the series.
     >>> print browser.url
     http://launchpad.dev/firefox/trunk
@@ -92,8 +89,7 @@
     >>> browser.getControl('Branch URL').value = (
     ...     'svn://svn.example.com/branch')
     >>> browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Code import created and branch linked to the series.
     >>> print browser.url
     http://launchpad.dev/firefox/trunk
@@ -108,8 +104,7 @@
     ...     'http://git.example.com/branch')
     >>> browser.getControl('Branch owner').value = ['hwdb-team']
     >>> browser.getControl('Update').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Code import created and branch linked to the series.
     >>> print browser.url
     http://launchpad.dev/firefox/trunk

=== modified file 'lib/lp/registry/stories/project/xx-project-add.txt'
--- lib/lp/registry/stories/project/xx-project-add.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/registry/stories/project/xx-project-add.txt	2013-09-27 04:45:05 +0000
@@ -25,7 +25,7 @@
   >>> admin_browser.getControl('Maintainer').value = 'cprov'
   >>> admin_browser.getControl('Homepage URL').value = 'www.kde.org'
   >>> admin_browser.getControl('Add').click()
-  >>> print "\n".join(get_feedback_messages(admin_browser.contents))
+  >>> print_feedback_messages(admin_browser.contents)
   There is 1 error.
   &quot;www.kde.org&quot; is not a valid URI
 
@@ -40,13 +40,13 @@
   >>> admin_browser.getControl('Maintainer').value = 'cprov'
   >>> admin_browser.getControl('Homepage URL').value = 'http://kde.org/'
   >>> admin_browser.getControl('Add').click()
-  >>> print "\n".join(get_feedback_messages(admin_browser.contents))
+  >>> print_feedback_messages(admin_browser.contents)
   There is 1 error.
   Invalid name 'kde!'. Names must...
 
   >>> admin_browser.getControl('Name', index=0).value = 'apache'
   >>> admin_browser.getControl('Add').click()
-  >>> print "\n".join(get_feedback_messages(admin_browser.contents))
+  >>> print_feedback_messages(admin_browser.contents)
   There is 1 error.
   apache is already used by another project
 

=== modified file 'lib/lp/registry/stories/team-polls/create-poll-options.txt'
--- lib/lp/registry/stories/team-polls/create-poll-options.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/team-polls/create-poll-options.txt	2013-09-27 04:45:05 +0000
@@ -55,7 +55,7 @@
     >>> team_admin_browser.getControl('Title').value = ''
     >>> team_admin_browser.getControl('Create').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     Required input is missing.
 
@@ -66,7 +66,7 @@
     >>> team_admin_browser.getControl('Title').value = 'Bill Again'
     >>> team_admin_browser.getControl('Create').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     ...is already in use by another option in this poll.
 
@@ -76,10 +76,10 @@
     >>> team_admin_browser.open(
     ...     'http://launchpad.dev/~ubuntu-team/+poll/director-2004/+newoption')
 
-    >>> "\n".join(get_feedback_messages(team_admin_browser.contents))
-    u'You can&#8217;t add new options because the poll is already closed.'
+    >>> print_feedback_messages(team_admin_browser.contents)
+    You can&#8217;t add new options because the poll is already closed.
 
     >>> team_admin_browser.open(
     ...     'http://launchpad.dev/~ubuntu-team/+poll/never-closes/+newoption')
-    >>> "\n".join(get_feedback_messages(team_admin_browser.contents))
-    u'You can&#8217;t add new options because the poll is already open.'
+    >>> print_feedback_messages(team_admin_browser.contents)
+    You can&#8217;t add new options because the poll is already open.

=== modified file 'lib/lp/registry/stories/team-polls/create-polls.txt'
--- lib/lp/registry/stories/team-polls/create-polls.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/registry/stories/team-polls/create-polls.txt	2013-09-27 04:45:05 +0000
@@ -54,7 +54,7 @@
     ...     name='field.datecloses').value = '2100-07-04 02:00:00+00:00'
     >>> team_admin_browser.getControl('Continue').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     Invalid name 'election_2100'. Names must be at least two characters ...
 
@@ -68,7 +68,7 @@
     ...     name='field.datecloses').value = '2100-06-04 02:00:00+00:00'
     >>> team_admin_browser.getControl('Continue').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     A poll cannot close at the time (or before) it opens.
 
@@ -147,7 +147,7 @@
     ...     name='field.datecloses').value = '2035-06-04 02:00:00+00:00'
     >>> team_admin_browser.getControl('Continue').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     dpl-2080 is already in use by another poll in this team.
 
@@ -160,6 +160,6 @@
     >>> today = datetime.today().strftime('%Y-%m-%d')
     >>> team_admin_browser.getControl(name='field.dateopens').value = today
     >>> team_admin_browser.getControl('Continue').click()
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     A poll cannot open less than 12 hours after it&#x27;s created.

=== modified file 'lib/lp/registry/stories/team-polls/edit-options.txt'
--- lib/lp/registry/stories/team-polls/edit-options.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/team-polls/edit-options.txt	2013-09-27 04:45:05 +0000
@@ -25,7 +25,7 @@
     ...     auth='Basic jeff.waugh@xxxxxxxxxxxxxxx:test')
     >>> browser.open('http://launchpad.dev/~ubuntu-team/+poll/never-closes4/'
     ...              '+option/20')
-    >>> print "\n".join(get_feedback_messages(browser.contents))
+    >>> print_feedback_messages(browser.contents)
     You can&#8217;t edit any options because the poll is already open.
 
 Since Jeff is an administrator of ubuntu-team and we have a poll that hasn't

=== modified file 'lib/lp/registry/stories/team-polls/edit-poll.txt'
--- lib/lp/registry/stories/team-polls/edit-poll.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/team-polls/edit-poll.txt	2013-09-27 04:45:05 +0000
@@ -28,7 +28,7 @@
     ...     'The unique name of this poll').value = 'never-closes'
     >>> team_admin_browser.getControl('Save').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     ...never-closes is already in use by another poll in this team.
 
@@ -43,7 +43,7 @@
     ...     name='field.datecloses').value = '3000-01-01 00:00:00+00:00'
     >>> team_admin_browser.getControl('Save').click()
 
-    >>> print "\n".join(get_feedback_messages(team_admin_browser.contents))
+    >>> print_feedback_messages(team_admin_browser.contents)
     There is 1 error.
     A poll cannot close at the time (or before) it opens.
 

=== modified file 'lib/lp/registry/stories/team-polls/vote-poll.txt'
--- lib/lp/registry/stories/team-polls/vote-poll.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/registry/stories/team-polls/vote-poll.txt	2013-09-27 04:45:05 +0000
@@ -162,6 +162,5 @@
 
     >>> team_admin_browser.open(
     ...     'http://launchpad.dev/~ubuntu-team/+poll/director-2004/+vote')
-    >>> for message in get_feedback_messages(team_admin_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(team_admin_browser.contents)
     This poll is already closed.

=== modified file 'lib/lp/registry/stories/team/xx-adminteammerge.txt'
--- lib/lp/registry/stories/team/xx-adminteammerge.txt	2012-01-15 13:32:27 +0000
+++ lib/lp/registry/stories/team/xx-adminteammerge.txt	2013-09-27 04:45:05 +0000
@@ -24,7 +24,7 @@
 
     >>> registry_browser.url
     'http://launchpad.dev/people/+adminteammerge'
-    >>> print get_feedback_messages(registry_browser.contents)[0]
+    >>> print_feedback_messages(registry_browser.contents)
     New Team has 1 active members which will have to be deactivated
     before the teams can be merged.
 
@@ -32,5 +32,5 @@
     >>> registry_browser.url
     'http://launchpad.dev/~guadamen'
 
-    >>> print get_feedback_messages(registry_browser.contents)[0]
+    >>> print_feedback_messages(registry_browser.contents)
     A merge is queued and is expected to complete in a few minutes.

=== modified file 'lib/lp/registry/stories/team/xx-team-add-my-teams.txt'
--- lib/lp/registry/stories/team/xx-team-add-my-teams.txt	2012-08-13 20:03:00 +0000
+++ lib/lp/registry/stories/team/xx-team-add-my-teams.txt	2013-09-27 04:45:05 +0000
@@ -33,8 +33,8 @@
     >>> browser.getControl('Continue').click()
     >>> browser.title
     'Hoary Gnome Team in Launchpad'
-    >>> print get_feedback_messages(browser.contents)
-    [u'Ubuntu Team has been proposed to this team.']
+    >>> print_feedback_messages(browser.contents)
+    Ubuntu Team has been proposed to this team.
     >>> print extract_text(
     ...     find_tag_by_id(browser.contents, 'recently-proposed'))
     Pending approval
@@ -61,8 +61,8 @@
     >>> user_browser.getControl('Continue').click()
     >>> print user_browser.title
     Super Team in Launchpad
-    >>> print get_feedback_messages(user_browser.contents)
-    [u'Sub Team has been added to this team because of an existing invite.']
+    >>> print_feedback_messages(user_browser.contents)
+    Sub Team has been added to this team because of an existing invite.
     >>> print extract_text(
     ...     find_tag_by_id(user_browser.contents, 'recently-approved'))
     Latest members
@@ -83,8 +83,8 @@
     >>> browser.getControl('Continue').click()
     >>> browser.title
     'Hoary Gnome Team in Launchpad'
-    >>> print get_feedback_messages(browser.contents)
-    [u'Ubuntu Team has been added to this team.']
+    >>> print_feedback_messages(browser.contents)
+    Ubuntu Team has been added to this team.
     >>> print extract_text(
     ...     find_tag_by_id(browser.contents, 'recently-approved'))
     Latest members
@@ -145,7 +145,7 @@
     >>> browser.getControl(name='field.teams').value = [
     ...     'guadamen', 'ubuntu-security']
     >>> browser.getControl('Continue').click()
-    >>> print "\n".join(get_feedback_messages(browser.contents))
+    >>> print_feedback_messages(browser.contents)
     GuadaMen and Ubuntu Security Team have been added to this team.
 
 And when no teams can be added, a message is displayed:

=== modified file 'lib/lp/registry/stories/team/xx-team-claim.txt'
--- lib/lp/registry/stories/team/xx-team-claim.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/registry/stories/team/xx-team-claim.txt	2013-09-27 04:45:05 +0000
@@ -45,8 +45,7 @@
     >>> user_browser.title
     'Ubuntu Doc Team does not use Launchpad'
 
-    >>> for msg in get_feedback_messages(user_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     A confirmation message has been sent to
     &#x27;doc@xxxxxxxxxxxxxxxx&#x27;. Follow the instructions in that
     message to finish claiming this team. (If the above address is from
@@ -96,8 +95,7 @@
 
     >>> user_browser.title
     'Ubuntu Doc Team in Launchpad'
-    >>> for msg in get_feedback_messages(user_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     Team claimed successfully
 
     >>> print extract_text(

=== modified file 'lib/lp/registry/stories/team/xx-team-contactemail.txt'
--- lib/lp/registry/stories/team/xx-team-contactemail.txt	2012-07-12 00:46:07 +0000
+++ lib/lp/registry/stories/team/xx-team-contactemail.txt	2013-09-27 04:45:05 +0000
@@ -49,8 +49,7 @@
     >>> browser.getControl('Change').click()
     >>> browser.title
     'Landscape Developers in Launchpad'
-    >>> for msg in get_feedback_messages(browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(browser.contents)
     A confirmation message has been sent to...
 
     >>> from lp.services.mail import stub
@@ -77,8 +76,7 @@
 
     >>> browser.title
     'Landscape Developers in Launchpad'
-    >>> for msg in get_feedback_messages(browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(browser.contents)
     Email address successfully confirmed.
 
     >>> browser.getLink(url='+contactaddress').click()

=== modified file 'lib/lp/registry/stories/teammembership/private-team.txt'
--- lib/lp/registry/stories/teammembership/private-team.txt	2012-04-26 00:13:47 +0000
+++ lib/lp/registry/stories/teammembership/private-team.txt	2013-09-27 04:45:05 +0000
@@ -63,8 +63,7 @@
     >>> user_browser.getLink('Subscribe someone else').click()
     >>> user_browser.getControl(name='field.person').value = 'private-team'
     >>> user_browser.getControl('Subscribe user').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     Invalid value
 
@@ -74,8 +73,7 @@
     >>> cprov_browser.getLink('Subscribe someone else').click()
     >>> cprov_browser.getControl(name='field.person').value = 'private-team'
     >>> cprov_browser.getControl('Subscribe user').click()
-    >>> for message in get_feedback_messages(cprov_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(cprov_browser.contents)
     Private Team team has been subscribed to this bug.
 
 The private team has been subscribed to a bug and although a regular user is
@@ -86,7 +84,6 @@
     >>> user_browser.getLink('Subscribe someone else').click()
     >>> user_browser.getControl(name='field.person').value = 'private-team'
     >>> user_browser.getControl('Subscribe user').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There is 1 error.
     Constraint not satisfied

=== modified file 'lib/lp/registry/stories/vouchers/xx-voucher-redemption.txt'
--- lib/lp/registry/stories/vouchers/xx-voucher-redemption.txt	2012-12-07 18:36:39 +0000
+++ lib/lp/registry/stories/vouchers/xx-voucher-redemption.txt	2013-09-27 04:45:05 +0000
@@ -72,8 +72,7 @@
     >>> browser.getControl('Redeem').click()
     >>> print browser.url
     http://launchpad.dev/%7Ecprov/+vouchers
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Voucher redeemed successfully
 
 
@@ -85,8 +84,7 @@
     >>> browser.getControl(name='field.voucher').value = [
     ...     'LPCBS12-f78df324-0cc2-11dd-8b6b-000000000005']
     >>> browser.getControl('Redeem').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     There is 1 error.
     Invalid value
 
@@ -129,6 +127,5 @@
     >>> browser.getControl(name='field.voucher').value = [
     ...     'LPCBS12-f78df324-0cc2-11dd-8b6b-com000000001']
     >>> browser.getControl('Redeem').click()
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Voucher redeemed successfully

=== modified file 'lib/lp/services/feeds/stories/xx-links.txt'
--- lib/lp/services/feeds/stories/xx-links.txt	2012-01-18 12:04:16 +0000
+++ lib/lp/services/feeds/stories/xx-links.txt	2013-09-27 04:45:05 +0000
@@ -68,8 +68,8 @@
     >>> auth_browser.open('http://feeds.launchpad.dev/bugs/14/bug.atom')
     >>> print auth_browser.url
     http://bugs.launchpad.dev/
-    >>> print get_feedback_messages(auth_browser.contents)
-    [u'The requested bug is private. Feeds do not serve private bugs.']
+    >>> print_feedback_messages(auth_browser.contents)
+    The requested bug is private. Feeds do not serve private bugs.
 
 == Latest Bugs and Branches for a Person ==
 
@@ -366,5 +366,5 @@
     >>> browser.open('http://feeds.launchpad.dev/~name12/landscape/feature-x/branch.atom')
     >>> print browser.url
     http://code.launchpad.dev/
-    >>> print get_feedback_messages(browser.contents)
-    [u'The requested branch is private. Feeds do not serve private branches.']
+    >>> print_feedback_messages(browser.contents)
+    The requested branch is private. Feeds do not serve private branches.

=== modified file 'lib/lp/services/oauth/stories/managing-tokens.txt'
--- lib/lp/services/oauth/stories/managing-tokens.txt	2012-01-15 11:06:57 +0000
+++ lib/lp/services/oauth/stories/managing-tokens.txt	2013-09-27 04:45:05 +0000
@@ -87,8 +87,7 @@
     >>> my_browser.getControl('Revoke Authorization', index=2).click()
     >>> print my_browser.title
     Authorized applications...
-    >>> for message in get_feedback_messages(my_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(my_browser.contents)
     Authorization revoked successfully.
 
     >>> my_browser.open('http://launchpad.dev/~salgado/+oauth-tokens')

=== modified file 'lib/lp/soyuz/stories/distroseries/add-architecture.txt'
--- lib/lp/soyuz/stories/distroseries/add-architecture.txt	2011-03-16 07:59:26 +0000
+++ lib/lp/soyuz/stories/distroseries/add-architecture.txt	2013-09-27 04:45:05 +0000
@@ -33,7 +33,7 @@
     >>> admin_browser.getLink('Add architecture').click()
     >>> admin_browser.getControl('Architecture Tag').value = 'foo bar'
     >>> admin_browser.getControl('Continue').click()
-    >>> print "\n".join(get_feedback_messages(admin_browser.contents))
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     Invalid name 'foo bar'. ...
 

=== modified file 'lib/lp/soyuz/stories/ppa/xx-copy-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-copy-packages.txt	2013-05-03 16:43:19 +0000
+++ lib/lp/soyuz/stories/ppa/xx-copy-packages.txt	2013-09-27 04:45:05 +0000
@@ -254,9 +254,7 @@
 
     >>> jblack_browser.getControl("Copy Packages").click()
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -399,9 +397,7 @@
 resulted binaries would conflict (same name and version, but different
 contents). So, this copy is not allowed.
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -421,9 +417,7 @@
 But this is also not allowed. Since pmount is still building in hoary,
 there are no binaries to be copied.
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -458,9 +452,7 @@
     ...     name='field.include_binaries').value = ['REBUILD_SOURCES']
     >>> jblack_browser.getControl("Copy Packages").click()
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -479,9 +471,7 @@
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -519,9 +509,7 @@
 The page not only renders the copy summary, but also shows the
 package copied in the available sources.
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -564,9 +552,7 @@
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -636,9 +622,7 @@
     ...     name='field.selected_sources').value = [pmount_pub_id]
     >>> jblack_browser.getControl('Destination series').value = ['warty']
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -658,9 +642,7 @@
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -754,9 +736,7 @@
 The page not only renders the copy summary, but also shows the
 package copied in the available sources.
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 2 packages to PPA for James Blackwell Friends.
     Please allow some time for these to be processed.
     >>> run_copy_jobs()
@@ -821,9 +801,7 @@
     >>> jblack_browser.getControl('Destination series').value = ['hoary']
 
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 2 packages to PPA for James Blackwell Friends.
     Please allow some time for these to be processed.
     >>> run_copy_jobs()
@@ -892,9 +870,7 @@
 0.1-1' source can be built in a archive, so the copy candidates are
 conflicts and cannot be allowed.
 
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 3 packages to PPA for James Blackwell Sandbox.
     Please allow some time for these to be processed.
     >>> run_copy_jobs()
@@ -946,9 +922,7 @@
 deleted files will be re-published in his archive.
 
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -1032,9 +1006,7 @@
     >>> jblack_browser.getControl(
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -1066,9 +1038,7 @@
     >>> jblack_browser.getControl(
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -1085,9 +1055,7 @@
     >>> jblack_browser.getControl(
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -1129,9 +1097,7 @@
     >>> jblack_browser.getControl(
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()
@@ -1165,9 +1131,7 @@
     >>> jblack_browser.getControl(
     ...     name='field.include_binaries').value = ['COPY_BINARIES']
     >>> jblack_browser.getControl("Copy Packages").click()
-    >>> messages = get_feedback_messages(jblack_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(jblack_browser.contents)
     Requested sync of 1 package to PPA for James Blackwell.
     Please allow some time for this to be processed.
     >>> run_copy_jobs()

=== modified file 'lib/lp/soyuz/stories/ppa/xx-delete-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-delete-packages.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/soyuz/stories/ppa/xx-delete-packages.txt	2013-09-27 04:45:05 +0000
@@ -107,9 +107,7 @@
     >>> admin_browser.getControl(name='field.name_filter').value = ''
     >>> admin_browser.getControl(name='field.selected_sources').value = ['31']
     >>> admin_browser.getControl("Request Deletion").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Source and binaries deleted by Foo Bar:
     iceweasel 1.0 in breezy-autotest
     Deletion comment: None
@@ -131,9 +129,7 @@
 
     >>> admin_browser.getControl("Deletion comment").value = "DO IT"
     >>> admin_browser.getControl("Request Deletion").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     No sources selected.
 
@@ -152,9 +148,7 @@
     ...     "Deletion comment").value = "DO <where is my XSS ?> IT"
     >>> admin_browser.getControl("Request Deletion").click()
 
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Source and binaries deleted by Foo Bar:
     cdrkit 1.0 in breezy-autotest
     Deletion comment: DO &lt;where is my XSS ?&gt; IT
@@ -231,9 +225,7 @@
     >>> admin_browser.getControl("Deletion comment").value = "DO IT AGAIN !"
     >>> admin_browser.getControl("Request Deletion").click()
 
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Source and binaries deleted by Foo Bar:
     iceweasel 1.0 in warty
     pmount 0.1-1 in warty
@@ -456,9 +448,7 @@
     >>> user_browser.getControl("Deletion comment").value = deletion_comment
     >>> user_browser.getControl("Request Deletion").click()
 
-    >>> messages = get_feedback_messages(user_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(user_browser.contents)
     Source and binaries deleted by No Privileges Person:
     foo 1.0 in hoary
     Deletion comment: Deletion of a number of base pairs that is ...

=== modified file 'lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt'
--- lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt	2012-11-26 01:02:38 +0000
+++ lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt	2013-09-27 04:45:05 +0000
@@ -104,9 +104,7 @@
 
     >>> admin_browser.getControl("Add PPA dependency").value = 'whatever'
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     Invalid value
 
@@ -116,9 +114,7 @@
 
     >>> admin_browser.getControl("Add PPA dependency").value = 'mark/ppa'
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Dependency added: PPA for Mark Shuttleworth
 
     >>> admin_browser.reload()
@@ -129,9 +125,7 @@
 
     >>> admin_browser.getControl("Add PPA dependency").value = 'mark/ppa'
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     This dependency is already registered.
 
@@ -140,9 +134,7 @@
 
     >>> admin_browser.getControl("Add PPA dependency").value = 'cprov/ppa'
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     An archive should not depend on itself.
 
@@ -150,9 +142,7 @@
 
     >>> admin_browser.getControl("Add PPA dependency").value = 'no-priv/ppa'
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Dependency added: PPA for No Privileges Person
 
 Now Celso's PPA will list Mark's and No-Priv's PPA as its dependencies.
@@ -234,7 +224,6 @@
 dependencies get removed.
 
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
 
     >>> print_ppa_dependencies(admin_browser.contents)
     PPA for Mark Shuttleworth
@@ -247,9 +236,7 @@
     ...     name="field.selected_dependencies").value = [
     ...     'mark/ppa', 'no-priv/ppa']
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Dependencies removed:
     PPA for Mark Shuttleworth
     PPA for No Privileges Person
@@ -307,9 +294,7 @@
     ...     "Proposed (default dependencies and proposed updates"
     ...     ).selected = True
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Primary dependency added: Primary Archive for Ubuntu Linux -
     PROPOSED (main, restricted, universe, multiverse)
 
@@ -329,9 +314,7 @@
     >>> admin_browser.getControl(
     ...     "Basic (only released packages).").selected = True
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Primary dependency added: Primary Archive for Ubuntu Linux -
     RELEASE (main, restricted, universe, multiverse)
 
@@ -359,9 +342,7 @@
     ...     "Default (security dependencies and recommended updates"
     ...     ).selected = True
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Default primary dependencies restored.
 
 The default option is now selected.
@@ -387,9 +368,7 @@
     ...     "primary archive."
     ...     ).selected = True
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Primary dependency added: Primary Archive for Ubuntu Linux - UPDATES
 
     >>> print_radio_button_field(
@@ -443,9 +422,7 @@
 
     >>> admin_browser.getControl("Add PPA dependency").value = 'no-priv/ppa'
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Dependency added: PPA for No Privileges Person
 
 Now the PPA uses the default primary dependency configuration and
@@ -484,9 +461,7 @@
     ...     "Basic (only released packages).").selected = True
 
     >>> admin_browser.getControl("Save").click()
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Primary dependency added:
        Primary Archive for Ubuntu Linux - RELEASE
        (main, restricted, universe, multiverse)

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-private-teams.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-private-teams.txt	2012-08-13 19:34:10 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-private-teams.txt	2013-09-27 04:45:05 +0000
@@ -77,7 +77,6 @@
 
    >>> admin_browser.getControl(name='field.private').value = False
    >>> admin_browser.getControl('Save').click()
-    >>> for error in get_feedback_messages(admin_browser.contents):
-    ...     print error
-    There is 1 error.
-    Private teams may not have public archives.
+   >>> print_feedback_messages(admin_browser.contents)
+   There is 1 error.
+   Private teams may not have public archives.

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt	2013-05-10 06:44:11 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt	2013-09-27 04:45:05 +0000
@@ -65,8 +65,7 @@
 
     >>> sample_browser.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(sample_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(sample_browser.contents)
     There is 1 error.
     Required input is missing.
 
@@ -79,8 +78,7 @@
     ...     name="field.displayname").value = 'Sample PPA'
     >>> sample_browser.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(sample_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(sample_browser.contents)
     There is 1 error.
     PPA Terms of Service must be accepted to activate a PPA.
 
@@ -147,8 +145,7 @@
     >>> print sample_browser.title
     Change details : Sample testing PPA...
 
-    >>> for error in get_feedback_messages(sample_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(sample_browser.contents)
     There is 1 error.
     Required input is missing.
 
@@ -392,8 +389,7 @@
     >>> print admin_browser.title
     Hack PPA : James Blackwell
 
-    >>> for msg in get_feedback_messages(admin_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     This PPA has been disabled.
 
 We need go back to the "Administer archive" page to see the build score and
@@ -412,8 +408,7 @@
     ...     name="field.external_dependencies"
     ...     ).value = "deb not_a_url"
     >>> admin_browser.getControl("Save").click()
-    >>> for error in get_feedback_messages(admin_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     &#x27;deb not_a_url&#x27; is not a complete and valid sources.list entry
 
@@ -442,8 +437,7 @@
     ...     name="field.authorized_size").value = str(limit + 1)
     >>> admin_browser.getControl("Save").click()
 
-    >>> for error in get_feedback_messages(admin_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(admin_browser.contents)
     There is 1 error.
     Value is too big
 
@@ -509,8 +503,7 @@
 
     >>> browser2.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(browser2.contents):
-    ...     print error
+    >>> print_feedback_messages(browser2.contents)
     There is 1 error.
     You already have a PPA named &#x27;boomppa&#x27;.
 
@@ -573,8 +566,7 @@
     ...     name="field.displayname").value = 'Edge PPA'
     >>> cprov_browser.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(cprov_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(cprov_browser.contents)
     There is 1 error.
     Required input is missing.
 
@@ -585,8 +577,7 @@
     >>> cprov_browser.getControl(name="field.name").value = 'ExPeRiMeNtAl!'
     >>> cprov_browser.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(cprov_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(cprov_browser.contents)
     There is 1 error.
     Invalid name 'experimental!'. Names must be at least two characters ...
 
@@ -596,8 +587,7 @@
     >>> cprov_browser.getControl(name="field.name").value = 'ppa'
     >>> cprov_browser.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(cprov_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(cprov_browser.contents)
     There is 1 error.
     You already have a PPA named &#x27;ppa&#x27;.
 
@@ -608,8 +598,7 @@
     >>> cprov_browser.getControl(name="field.name").value = 'ubuntu'
     >>> cprov_browser.getControl("Activate").click()
 
-    >>> for error in get_feedback_messages(cprov_browser.contents):
-    ...     print error
+    >>> print_feedback_messages(cprov_browser.contents)
     There is 1 error.
     A PPA cannot have the same name as its distribution.
 
@@ -788,8 +777,7 @@
     >>> print no_priv_browser.url
     http://launchpad.dev/~no-priv
 
-    >>> for msg in get_feedback_messages(no_priv_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(no_priv_browser.contents)
     Deletion of &#x27;PPA for No Privileges Person&#x27; has been
     requested and the repository will be removed shortly.
 

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt	2013-09-27 04:45:05 +0000
@@ -78,8 +78,7 @@
     Name                Expires     Comment
     Joe Smith                       Joe is my friend    Edit/Cancel
 
-    >>> for msg in get_feedback_messages(cprov_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(cprov_browser.contents)
     You have granted access for Joe Smith to install software from
     PPA named p3a for Celso Providelo. Joe Smith will be notified of the
     access via email.
@@ -112,8 +111,7 @@
     Launchpad Developers    2200-08-01    Launchpad developer access.
     ...
 
-    >>> for msg in get_feedback_messages(cprov_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(cprov_browser.contents)
     You have granted access for Launchpad Developers to install software
     from PPA named p3a for Celso Providelo. Members of Launchpad Developers
     will be notified of the access via email.
@@ -153,8 +151,7 @@
     Joe Smith                             Joe is my friend    ...
     Launchpad Developers    2200-08-01    a different description
     ...
-    >>> for msg in get_feedback_messages(cprov_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(cprov_browser.contents)
     The access for Launchpad Developers has been updated.
 
 === Scenario 2: Canceling a subscription ===
@@ -183,8 +180,7 @@
     Name                    Expires       Comment
     Joe Smith                             Joe is my friend    Edit/Cancel
 
-    >>> for msg in get_feedback_messages(cprov_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(cprov_browser.contents)
     You have revoked Launchpad Developers&#x27;s access to PPA
     named p3a for Celso Providelo.
 
@@ -260,8 +256,7 @@
 the page is redisplayed with new sources.list entries and a notification.
 
     >>> joe_browser.getControl(name='regenerate_btn').click()
-    >>> for msg in get_feedback_messages(joe_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(joe_browser.contents)
     Launchpad has generated the new password you requested for your
     access to the archive PPA named p3a for Celso Providelo. Please
     follow the instructions below to update your custom

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt	2013-05-09 08:53:01 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt	2013-09-27 04:45:05 +0000
@@ -24,8 +24,7 @@
     >>> print cprov_browser.url
     http://launchpad.dev/~cprov/+archive/ppa
 
-    >>> for msg in get_feedback_messages(cprov_browser.contents):
-    ...     print msg
+    >>> print_feedback_messages(cprov_browser.contents)
     Only private archives can have subscribers.
 
 Setup private PPAs for both Celso and Mark:

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-build-record.txt'
--- lib/lp/soyuz/stories/soyuz/xx-build-record.txt	2013-08-29 18:34:59 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-build-record.txt	2013-09-27 04:45:05 +0000
@@ -137,8 +137,7 @@
 Once submitted they are redirected to the build index page where the
 new 'score' value is presented.
 
-    >>> for text in get_feedback_messages(admin_browser.contents):
-    ...     print text
+    >>> print_feedback_messages(admin_browser.contents)
     Build rescored to 0.
 
     >>> print extract_text(find_tag_by_id(admin_browser.contents, 'status'))
@@ -288,8 +287,7 @@
 
     >>> admin_browser.getLink("Retry this build").click()
     >>> admin_browser.getControl("Retry Build").click()
-    >>> for text in get_feedback_messages(admin_browser.contents):
-    ...     print text
+    >>> print_feedback_messages(admin_browser.contents)
     Build retried
 
     >>> print extract_text(find_tag_by_id(admin_browser.contents, 'status'))

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-builder-page.txt'
--- lib/lp/soyuz/stories/soyuz/xx-builder-page.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-builder-page.txt	2013-09-27 04:45:05 +0000
@@ -55,9 +55,7 @@
 The builder status is now displayed as normal text and not as as a
 notification alert.
 
-    >>> messages = get_feedback_messages(anon_browser.contents)
-    >>> print messages
-    []
+    >>> print_feedback_messages(anon_browser.contents)
 
 == Builder Actions ==
 
@@ -163,9 +161,8 @@
 
 And a relevant notification is displayed after the mode toggle.
 
-    >>> messages = get_feedback_messages(cprov_browser.contents)
-    >>> print messages
-    [u'The builder &quot;Bob The Builder&quot; was updated successfully.']
+    >>> print_feedback_messages(cprov_browser.contents)
+    The builder &quot;Bob The Builder&quot; was updated successfully.
 
 Via the 'edit' form Celso can also modify the 'builderok',
 'failure_notes', 'virtualized' and 'virtual machine' fields. All the
@@ -183,9 +180,8 @@
 Changing the details via the Change details page also generates a
 notification.
 
-    >>> messages = get_feedback_messages(cprov_browser.contents)
-    >>> print messages
-    [u'The builder &quot;Bob The Builder&quot; was updated successfully.']
+    >>> print_feedback_messages(cprov_browser.contents)
+    The builder &quot;Bob The Builder&quot; was updated successfully.
 
 
 == Marking a builder as inactive ==

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt	2013-07-22 17:02:19 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt	2013-09-27 04:45:05 +0000
@@ -127,8 +127,7 @@
 
 There's also a notification message announcing the success of the change:
 
-    >>> for message in get_feedback_messages(admin_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(admin_browser.contents)
     Successfully updated
 
 Navigating back to the admin page shows the change was made:

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-person-packages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-person-packages.txt	2013-07-16 08:10:32 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-person-packages.txt	2013-09-27 04:45:05 +0000
@@ -396,9 +396,7 @@
     ...     "Deletion comment").value = "Bug 184490"
     >>> admin_browser.getControl("Request Deletion").click()
 
-    >>> messages = get_feedback_messages(admin_browser.contents)
-    >>> for msg in messages:
-    ...     print msg
+    >>> print_feedback_messages(admin_browser.contents)
     Source and binaries deleted by Foo Bar:
     source2 666 in breezy-autotest
     Deletion comment: Bug 184490

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt	2013-05-28 08:49:38 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt	2013-09-27 04:45:05 +0000
@@ -53,8 +53,7 @@
 
     >>> motu_browser.getControl(name="QUEUE_ID").value = ['4']
     >>> motu_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(motu_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     FAILED: alsa-utils (You have no rights to accept component(s) &#x27;main&#x27;)
 
 The same applies to the binary upload "pmount" because its build
@@ -62,8 +61,7 @@
 
     >>> motu_browser.getControl(name="QUEUE_ID").value=['2']
     >>> motu_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(motu_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     FAILED: pmount (You have no rights to accept component(s) &#x27;main&#x27;)
 
 Let's change the components on some uploads so that the user has
@@ -109,8 +107,7 @@
     >>> motu_browser.getControl(
     ...     name="component_override").displayValue = ["main"]
     >>> motu_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(motu_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     FAILED: alsa-utils (No rights to override to main)
 
 The same applies to the binary:
@@ -119,8 +116,7 @@
     >>> motu_browser.getControl(
     ...     name="component_override").displayValue = ["main"]
     >>> motu_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(motu_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     FAILED: pmount (No rights to override to main)
 
 Our user is able to override to multiverse, however.  Let's do that
@@ -130,8 +126,7 @@
     >>> motu_browser.getControl(
     ...     name="component_override").displayValue = ["multiverse"]
     >>> motu_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(motu_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     OK: pmount(multiverse/(unchanged)/(unchanged))
 
 Our user is also able to reject, let's reject alsa-utils:
@@ -139,8 +134,7 @@
     >>> motu_browser.getControl(name="QUEUE_ID").value = ['4']
     >>> motu_browser.getControl(name='rejection_comment').value = 'Foo'
     >>> motu_browser.getControl(name="Reject").click()
-    >>> for message in get_feedback_messages(motu_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     OK: alsa-utils
 
 In some cases the user might select more than one item at once, but he
@@ -152,7 +146,6 @@
     >>> motu_browser.getControl(
     ...     name="component_override").displayValue = ["multiverse"]
     >>> motu_browser.getControl(name="Accept").click()
-    >>> for message in sorted(get_feedback_messages(motu_browser.contents)):
-    ...     print message
+    >>> print_feedback_messages(motu_browser.contents)
     FAILED: netapplet (You have no rights to accept component(s) &#x27;main&#x27;)
     OK: mozilla-firefox(multiverse/(unchanged)/(unchanged))

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2013-07-25 11:58:55 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2013-09-27 04:45:05 +0000
@@ -259,8 +259,7 @@
     >>> anon_browser.getControl(
     ...     name="queue_state", index=0).displayValue = ['Accepted']
     >>> anon_browser.getControl("Update").click()
-    >>> for message in get_feedback_messages(anon_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(anon_browser.contents)
     The Accepted queue is empty.
 
 Now we act on the queue, which requires admin or upload_manager permission.
@@ -362,9 +361,7 @@
     >>> duplicate_submission_browser.getControl(
     ...     name="QUEUE_ID").value = [str(bar_queue_id)]
     >>> duplicate_submission_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(
-    ...     duplicate_submission_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(duplicate_submission_browser.contents)
     FAILED: bar (Unable to accept queue item due to status.)
 
 No emails are sent in this case:
@@ -380,8 +377,7 @@
     ...    "http://localhost/ubuntu/breezy-autotest/+queue";)
     >>> upload_manager_browser.getControl(name="QUEUE_ID").value = ['1']
     >>> upload_manager_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(upload_manager_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(upload_manager_browser.contents)
     OK: mozilla-firefox
 
 The item is moved to the ACCEPTED queue:
@@ -418,8 +414,7 @@
 
     >>> upload_manager_browser.getControl(name="QUEUE_ID").value = ['9']
     >>> upload_manager_browser.getControl(name="Accept").click()
-    >>> for message in get_feedback_messages(upload_manager_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(upload_manager_browser.contents)
     OK: cnews
 
 And the page is still on the Unapproved list:
@@ -470,9 +465,7 @@
 He sees the informational message that confirms the details of what was
 overridden:
 
-    >>> for message in sorted(get_feedback_messages(
-    ...     upload_manager_browser.contents)):
-    ...     print message
+    >>> print_feedback_messages(upload_manager_browser.contents)
     OK: netapplet(restricted/admin)
     OK: pmount(restricted/admin/extra)
 
@@ -524,8 +517,7 @@
     >>> upload_manager_browser.getControl(name='rejection_comment').value = (
     ...     'Foo')
     >>> upload_manager_browser.getControl(name="Reject").click()
-    >>> for message in get_feedback_messages(upload_manager_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(upload_manager_browser.contents)
     OK: alsa-utils
 
     >>> print_queue(upload_manager_browser.contents)

=== modified file 'lib/lp/testing/browser.py'
--- lib/lp/testing/browser.py	2012-06-29 08:40:05 +0000
+++ lib/lp/testing/browser.py	2013-09-27 04:45:05 +0000
@@ -39,7 +39,7 @@
     extract_text,
     find_main_content,
     find_tag_by_id,
-    get_feedback_messages,
+    print_feedback_messages,
     )
 
 
@@ -145,7 +145,7 @@
     test.globs['browser'] = Browser()
     test.globs['find_tag_by_id'] = find_tag_by_id
     test.globs['find_main_content'] = find_main_content
-    test.globs['get_feedback_messages'] = get_feedback_messages
+    test.globs['print_feedback_messages'] = print_feedback_messages
     test.globs['extract_text'] = extract_text
 
 

=== modified file 'lib/lp/testing/doc/pagetest-helpers.txt'
--- lib/lp/testing/doc/pagetest-helpers.txt	2012-07-06 06:02:33 +0000
+++ lib/lp/testing/doc/pagetest-helpers.txt	2013-09-27 04:45:05 +0000
@@ -424,14 +424,14 @@
     TEXT: "text with spaces"
 
 
-get_feedback_messages
+print_feedback_messages
 ---------------------
 
 When testing an error condition or a notification we often are only
 interested in the feedback messages.  This helper will get informational
 messages and error messages, based on the CSS class.
 
-    >>> get_feedback_messages = test.globs['get_feedback_messages']
+    >>> print_feedback_messages = test.globs['print_feedback_messages']
     >>> class FakeBrowser:
     ...     pass
     >>> browser = FakeBrowser()
@@ -442,8 +442,9 @@
     ...   <div class="error message">Red Alert!</div>
     ... </html>'''
 
-    >>> get_feedback_messages(browser.contents)
-    [u'1 file has been deleted.', u'Red Alert!']
+    >>> print_feedback_messages(browser.contents)
+    1 file has been deleted.
+    Red Alert!
 
 The helper extracts the text of the messages, which makes a difference
 if the messages contain html elements.
@@ -458,8 +459,9 @@
     ...   </div>
     ... </html>'''
 
-    >>> get_feedback_messages(browser.contents)
-    [u'1 file has been deleted.', u'Red Alert!  There are more details.']
+    >>> print_feedback_messages(browser.contents)
+    1 file has been deleted.
+    Red Alert!  There are more details.
 
 
 print_radio_button_field

=== modified file 'lib/lp/testing/pages.py'
--- lib/lp/testing/pages.py	2013-04-22 09:11:01 +0000
+++ lib/lp/testing/pages.py	2013-09-27 04:45:05 +0000
@@ -281,7 +281,7 @@
 def print_feedback_messages(content):
     """Print out the feedback messages."""
     for message in get_feedback_messages(content):
-        print message
+        print extract_text(message)
 
 
 def print_table(content, columns=None, skip_rows=None, sep="\t"):
@@ -798,7 +798,6 @@
     test.globs['find_tags_by_class'] = find_tags_by_class
     test.globs['find_portlet'] = find_portlet
     test.globs['find_main_content'] = find_main_content
-    test.globs['get_feedback_messages'] = get_feedback_messages
     test.globs['print_feedback_messages'] = print_feedback_messages
     test.globs['print_table'] = print_table
     test.globs['extract_link_from_tag'] = extract_link_from_tag

=== modified file 'lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt'
--- lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt	2013-04-11 00:51:46 +0000
+++ lib/lp/translations/stories/distroseries/xx-distroseries-language-packs.txt	2013-09-27 04:45:05 +0000
@@ -104,7 +104,7 @@
     >>> admin_browser.getControl('Change').click()
     >>> print admin_browser.url
     http://translations.launchpad.dev/ubuntu/hoary/+language-packs
-    >>> print "\n".join(get_feedback_messages(admin_browser.contents))
+    >>> print_feedback_messages(admin_browser.contents)
     Your request has been noted. Next language pack export will include
     all available translations...
 

=== modified file 'lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt'
--- lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt	2013-04-11 01:27:33 +0000
+++ lib/lp/translations/stories/importqueue/xx-translation-import-queue.txt	2013-09-27 04:45:05 +0000
@@ -444,10 +444,7 @@
   >>> evo_owner_browser.getControl('Upload').click()
   >>> evo_owner_browser.url
   'http://translations.launchpad.dev/evolution/trunk/+translations-upload'
-  >>> msgs = get_feedback_messages(evo_owner_browser.contents)
-  >>> print len(msgs)
-  1
-  >>> print msgs[0]
+  >>> print_feedback_messages(evo_owner_browser.contents)
   Upload ignored.  The tarball you uploaded did not contain...
 
 Bad filter_extension

=== modified file 'lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt'
--- lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt	2009-09-18 15:42:19 +0000
+++ lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt	2013-09-27 04:45:05 +0000
@@ -55,8 +55,7 @@
     >>> user_browser.getControl('Format:').clear() 
     >>> user_browser.getControl('Request Download').click()
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Please select a valid format for download.
 
 The most usual and most well-supported format is PO.
@@ -67,8 +66,7 @@
     >>> print user_browser.url
     http://translations.launchpad.dev/evolution/trunk
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Your request has been received. Expect to receive an email shortly.
 
 An alternative is MO.
@@ -79,8 +77,7 @@
     >>> print user_browser.url
     http://translations.launchpad.dev/evolution/trunk
 
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Your request has been received. Expect to receive an email shortly.
 
 
@@ -90,8 +87,7 @@
 the option to download any.
 
     >>> user_browser.open('http://translations.launchpad.dev/bzr/trunk/+export')
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     There are no translations to download in Bazaar trunk series.
 
 On +translate pages for products that do not have any translations, the action

=== modified file 'lib/lp/translations/stories/productseries/xx-productseries-translations-bzr-request.txt'
--- lib/lp/translations/stories/productseries/xx-productseries-translations-bzr-request.txt	2012-11-08 23:58:19 +0000
+++ lib/lp/translations/stories/productseries/xx-productseries-translations-bzr-request.txt	2013-09-27 04:45:05 +0000
@@ -58,7 +58,7 @@
     >>> browser.getControl("Request one-time import").click()
     >>> print browser.url
     http://translations.launchpad.dev/evolution/trunk
-    >>> print "\n".join(get_feedback_messages(browser.contents))
+    >>> print_feedback_messages(browser.contents)
     The import has been requested.
 
 == The request page without a branch ==

=== modified file 'lib/lp/translations/stories/productseries/xx-productseries-translations-settings.txt'
--- lib/lp/translations/stories/productseries/xx-productseries-translations-settings.txt	2012-11-08 23:58:19 +0000
+++ lib/lp/translations/stories/productseries/xx-productseries-translations-settings.txt	2013-09-27 04:45:05 +0000
@@ -88,7 +88,7 @@
 
     >>> print browser.url
     http://translations.launchpad.dev/evolution/trunk/
-    >>> print "\n".join(get_feedback_messages(browser.contents))
+    >>> print_feedback_messages(browser.contents)
     The settings have been updated.
 
 If he looks at the synchonization settings page again, he sees that

=== modified file 'lib/lp/translations/stories/standalone/xx-licensing.txt'
--- lib/lp/translations/stories/standalone/xx-licensing.txt	2012-05-24 20:25:54 +0000
+++ lib/lp/translations/stories/standalone/xx-licensing.txt	2013-09-27 04:45:05 +0000
@@ -36,8 +36,8 @@
 
 Karl sees a notice acknowledging his choice.
 
-    >>> get_feedback_messages(browser.contents)
-    [u'We respect your choice...']
+    >>> print_feedback_messages(browser.contents)
+    We respect your choice...
 
 He's also forwarded back to the Spanish alsa-utils translations page.
 
@@ -75,8 +75,8 @@
     >>> radiobuttons.value = ['BSD']
     >>> browser.getControl("Confirm").click()
 
-    >>> get_feedback_messages(browser.contents)
-    [u'Thank you for BSD-licensing your translations.']
+    >>> print_feedback_messages(browser.contents)
+    Thank you for BSD-licensing your translations.
 
 Karl can now browse to a +translate page without being forwarded.
 

=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-details.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-details.txt	2011-05-27 19:53:20 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-details.txt	2013-09-27 04:45:05 +0000
@@ -115,17 +115,17 @@
 
     >>> anon_browser.open('http://translations.launchpad.dev/evolution/trunk/'
     ...                   '+pots/evolution-2.2/es/+filter?person=danilo')
-    >>> get_feedback_messages(anon_browser.contents)
-    [u'Requested person not found.',
-    u'This person has made no contributions to this file.']
+    >>> print_feedback_messages(anon_browser.contents)
+    Requested person not found.
+    This person has made no contributions to this file.
 
 If a person to filter by is not specified, user is notified of that.
 
     >>> anon_browser.open('http://translations.launchpad.dev/evolution/trunk/'
     ...                   '+pots/evolution-2.2/es/+filter')
-    >>> get_feedback_messages(anon_browser.contents)
-    [u'No person to filter by specified.',
-    u'This person has made no contributions to this file.']
+    >>> print_feedback_messages(anon_browser.contents)
+    No person to filter by specified.
+    This person has made no contributions to this file.
 
 
 Merged accounts

=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt	2013-04-11 00:51:46 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt	2013-09-27 04:45:05 +0000
@@ -116,8 +116,7 @@
 suggestions will be shown only if he adds Japanese to his preferred languages
 first.
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Not showing suggestions from selected alternative language Japanese (ja).
     If you wish to see suggestions from this language, add it to your
     preferred languages first.

=== modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate.txt'
--- lib/lp/translations/stories/standalone/xx-pofile-translate.txt	2010-12-02 16:13:51 +0000
+++ lib/lp/translations/stories/standalone/xx-pofile-translate.txt	2013-09-27 04:45:05 +0000
@@ -229,13 +229,11 @@
 
     >>> browser.open('http://translations.launchpad.dev/ubuntu/hoary/'
     ...     '+source/evolution/+pots/evolution-2.2/ab/+translate')
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Launchpad can&#8217;t handle the plural items ...
 
     >>> browser.open('http://translations.launchpad.dev/ubuntu/hoary/'
     ...     '+source/evolution/+pots/evolution-2.2/ab/5/+translate')
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print extract_text(message)
+    >>> print_feedback_messages(browser.contents)
     Launchpad can&#8217;t handle the plural items ...
 

=== modified file 'lib/lp/translations/stories/standalone/xx-potemplate-export.txt'
--- lib/lp/translations/stories/standalone/xx-potemplate-export.txt	2009-12-10 12:46:11 +0000
+++ lib/lp/translations/stories/standalone/xx-potemplate-export.txt	2013-09-27 04:45:05 +0000
@@ -27,8 +27,7 @@
   >>> browser.getControl('Format:').clear() 
   >>> browser.getControl('Request Download').click()
 
-  >>> for message in get_feedback_messages(browser.contents):
-  ...     print message
+  >>> print_feedback_messages(browser.contents)
   Please select a valid format for download.
 
 If we choose 'Selected files' but don't select any PO file, we should receive
@@ -38,8 +37,7 @@
   >>> browser.getControl('Format:').value = ['PO']
   >>> browser.getControl('Request Download').click()
 
-  >>> for message in get_feedback_messages(browser.contents):
-  ...     print message
+  >>> print_feedback_messages(browser.contents)
   Please select at least one translation or template.
 
 If we POST the page with valid data, it should add the request to the queue.
@@ -52,8 +50,7 @@
   >>> print browser.url
   http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2
 
-  >>> for message in get_feedback_messages(browser.contents):
-  ...     print message
+  >>> print_feedback_messages(browser.contents)
   Your request has been received. Expect to receive an email shortly.
 
 We can also request only the PO Template. By doing this we're basically adding
@@ -68,7 +65,6 @@
   >>> print browser.url
   http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2
 
-  >>> for message in get_feedback_messages(browser.contents):
-  ...     print message
+  >>> print_feedback_messages(browser.contents)
   Your request has been received. Expect to receive an email shortly.
 

=== modified file 'lib/lp/translations/stories/standalone/xx-product-export.txt'
--- lib/lp/translations/stories/standalone/xx-product-export.txt	2011-12-29 05:29:36 +0000
+++ lib/lp/translations/stories/standalone/xx-product-export.txt	2013-09-27 04:45:05 +0000
@@ -29,8 +29,7 @@
     Download : Translations...
 
     >>> user_browser.getControl('Request Download').click()
-    >>> for message in get_feedback_messages(user_browser.contents):
-    ...     print message
+    >>> print_feedback_messages(user_browser.contents)
     Your request has been received.  Expect to receive an email shortly.
 
 

=== modified file 'lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt'
--- lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt	2012-01-15 13:32:27 +0000
+++ lib/lp/translations/stories/standalone/xx-sourcepackage-export.txt	2013-09-27 04:45:05 +0000
@@ -173,8 +173,7 @@
     >>> print browser.url
     http://translations.launchpad.dev/ubuntu/hoary/+source/mozilla
 
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     Your request has been received. Expect to receive an email shortly.
 
 
@@ -204,8 +203,7 @@
     >>> browser.open(
     ...     'http://translations.launchpad.dev/'
     ...     'ubuntu/hoary/+source/evolution/+export')
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
 
     >>> an_evolution_template.source_file_format = TranslationFileFormat.MO
     >>> flush_database_updates()
@@ -213,8 +211,7 @@
     >>> browser.open(
     ...     'http://translations.launchpad.dev/'
     ...     'ubuntu/hoary/+source/evolution/+export')
-    >>> for message in get_feedback_messages(browser.contents):
-    ...     print message
+    >>> print_feedback_messages(browser.contents)
     This package has templates with different native file formats.  If you
     proceed, all translations will be exported in the single format you
     specify.


Follow ups