← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:blueprints-pagetests-future-imports into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:blueprints-pagetests-future-imports into launchpad:master.

Commit message:
Convert lp.blueprints pagetests to preferred __future__ imports

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/390550

This is almost entirely formulaic, but xx-non-ascii-imagemap.txt needed some manual adjustment to avoid confusion about the encoding of Unicode string literals.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:blueprints-pagetests-future-imports into launchpad:master.
diff --git a/lib/lp/blueprints/browser/tests/sprintattendance-views.txt b/lib/lp/blueprints/browser/tests/sprintattendance-views.txt
index 4473723..ea15513 100644
--- a/lib/lp/blueprints/browser/tests/sprintattendance-views.txt
+++ b/lib/lp/blueprints/browser/tests/sprintattendance-views.txt
@@ -21,10 +21,10 @@ The view captures the user's time_start and time_ends attendance.
 
 The view also defines a next_url and cancel_url.
 
-    >>> print sprint_attendance_view.next_url
+    >>> print(sprint_attendance_view.next_url)
     http://launchpad.test/sprints/ubz
 
-    >>> print sprint_attendance_view.cancel_url
+    >>> print(sprint_attendance_view.cancel_url)
     http://launchpad.test/sprints/ubz
 
 A helper function to test date handling.
@@ -57,7 +57,7 @@ message.
 
     >>> dates = ['2005-11-15', '2005-10-09']
     >>> sprint_attendance_view = create_sprint_attendance_view(ubz, dates)
-    >>> print sprint_attendance_view.getFieldError('time_ends')
+    >>> print(sprint_attendance_view.getFieldError('time_ends'))
     The end time must be after the start time.
 
 Choosing a starting date too far after the meeting's end returns an
@@ -65,7 +65,7 @@ error message.
 
     >>> dates = ['2006-01-01', '2006-02-01']
     >>> sprint_attendance_view = create_sprint_attendance_view(ubz, dates)
-    >>> print sprint_attendance_view.getFieldError('time_starts')
+    >>> print(sprint_attendance_view.getFieldError('time_starts'))
     Please pick a date before 2005-11-16 19:11
 
 Choosing a ending date more than a day before the meeting's start
@@ -73,7 +73,7 @@ returns an error message.
 
     >>> dates = ['2005-07-01', '2005-08-01']
     >>> sprint_attendance_view = create_sprint_attendance_view(ubz, dates)
-    >>> print sprint_attendance_view.getFieldError('time_ends')
+    >>> print(sprint_attendance_view.getFieldError('time_ends'))
     Please pick a date after 2005-10-07 19:30
 
 Entering a starting date just before the meeting's start date or a
@@ -148,7 +148,7 @@ The +attend view
 The +attend view has a label.
 
     >>> sprint_attendance_view = create_view(ubz, name='+attend')
-    >>> print sprint_attendance_view.label
+    >>> print(sprint_attendance_view.label)
     Register your attendance
 
 
@@ -158,7 +158,7 @@ The +register views
 The +register view has a label too.
 
     >>> view = create_view(ubz, name='+register')
-    >>> print view.label
+    >>> print(view.label)
     Register someone else
 
 The view descends from BaseSprintAttendanceAddView.
@@ -184,7 +184,7 @@ else.
     []
 
     >>> for attendee in ubz.attendees:
-    ...     print attendee.name
+    ...     print(attendee.name)
     black brown greene name12
 
 
@@ -199,8 +199,8 @@ include it.
 
     >>> view = create_view(ubz, '+attendees-csv')
     >>> lines =  view.render().strip().splitlines()
-    >>> print lines[0]
+    >>> print(lines[0])
     Launchpad username,Display name,...Timezone,...Physically present
 
-    >>> print lines[-1]
+    >>> print(lines[-1])
     name12,Sample Person,...Australia/Perth,...True
diff --git a/lib/lp/blueprints/stories/blueprints/xx-buglinks.txt b/lib/lp/blueprints/stories/blueprints/xx-buglinks.txt
index 88f89ae..6c4e692 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-buglinks.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-buglinks.txt
@@ -11,7 +11,7 @@ the 'Related Bugs' section.
 
     >>> anon_browser.open(
     ...     'http://launchpad.test/firefox/+spec/svg-support')
-    >>> print extract_text(find_tag_by_id(anon_browser.contents, 'bug_links'))
+    >>> print(extract_text(find_tag_by_id(anon_browser.contents, 'bug_links')))
     Related bugs
     Bug #1: Firefox does not support SVG   New
 
diff --git a/lib/lp/blueprints/stories/blueprints/xx-creation.txt b/lib/lp/blueprints/stories/blueprints/xx-creation.txt
index a9e3e31..8eeb212 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-creation.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-creation.txt
@@ -65,7 +65,7 @@ Starting from the Blueprints home page:
 
 Users can press the graphical "Register a blueprint" button:
 
-    >>> print find_tag_by_id(user_browser.contents, 'addspec')
+    >>> print(find_tag_by_id(user_browser.contents, 'addspec'))
     <a href="+new" id="addspec"> <img alt="Register a blueprint"...
 
 
@@ -79,15 +79,15 @@ Starting from the Ubuntu distribution page:
 Users can use the ISpecificationTarget involvement menu to register a
 blueprint.
 
-    >>> print extract_text(
-    ...     find_tag_by_id(user_browser.contents, 'involvement').a)
+    >>> print(extract_text(
+    ...     find_tag_by_id(user_browser.contents, 'involvement').a))
     Register a blueprint
 
 Users can also follow the textual "Register a blueprint" link:
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'menu-link-new'):
-    ...     print tag
+    ...     print(tag)
     <a class="menu-link-new..."
        href="http://blueprints.launchpad.test/ubuntu/+addspec";>Register
        a blueprint</a>
@@ -104,7 +104,7 @@ Users can also follow the textual "Register a blueprint" link:
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'menu-link-new'):
-    ...     print tag
+    ...     print(tag)
     <a class="menu-link-new..."
        href="http://blueprints.launchpad.test/ubuntu/hoary/+addspec";>Register
        a blueprint</a>
@@ -122,7 +122,7 @@ Users can also follow the textual "Register a blueprint" link:
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'menu-link-new'):
-    ...     print tag
+    ...     print(tag)
     <a class="menu-link-new..."
        href="http://blueprints.launchpad.test/bzr/+addspec";>Register
        a blueprint</a>
@@ -131,11 +131,11 @@ For products without any blueprints, users can follow the special "register
 it here as a blueprint" link:
 
     >>> user_browser.getLink('register it here as a blueprint').click()
-    >>> print user_browser.url
+    >>> print(user_browser.url)
     http://blueprints.launchpad.test/bzr/+addspec
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Register a blueprint in...
-    >>> print extract_text(find_main_content(user_browser.contents))
+    >>> print(extract_text(find_main_content(user_browser.contents)))
     Register a new blueprint...
 
 From a product series
@@ -150,7 +150,7 @@ Users can also follow the textual "Register a blueprint" link:
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'menu-link-new'):
-    ...     print tag
+    ...     print(tag)
     <a class="menu-link-new..."
        href="http://blueprints.launchpad.test/firefox/1.0/+addspec";>Register
        a blueprint</a>
@@ -167,7 +167,7 @@ Users can follow the textual "Register a blueprint" link:
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'menu-link-new'):
-    ...     print tag
+    ...     print(tag)
     <a class="menu-link-new..."
        href="http://blueprints.launchpad.test/mozilla/+addspec";>Register
        a blueprint</a>
@@ -184,7 +184,7 @@ Users can also follow the textual "Register a blueprint" link:
 
     >>> for tag in find_tags_by_class(
     ...     user_browser.contents, 'menu-link-new'):
-    ...     print tag
+    ...     print(tag)
     <a class="menu-link-new..."
      href="http://blueprints.launchpad.test/sprints/futurista/+addspec";>Register
        a blueprint</a>
@@ -249,7 +249,7 @@ Ubuntu distribution, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Network Magic: Auto Network Detection...
 
 
@@ -273,7 +273,7 @@ Ubuntu distribution, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic-1'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Network Magic: Auto Network Detection...
 
 
@@ -310,12 +310,12 @@ Ubuntu distribution, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic-2'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Network Magic: Auto Network Detection...
 
 The new blueprint is not proposed as a series goal:
 
-    >>> print user_browser.getLink('Propose as goal')
+    >>> print(user_browser.getLink('Propose as goal'))
     <Link ...Propose as goal...
 
 Let's register another blueprint from the Mozilla Firefox 1.0 product series:
@@ -337,13 +337,13 @@ Ubuntu distribution, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+spec/networkmagic-3'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Network Magic: Auto Network Detection...
 
 As requested, the new blueprint is proposed as a series goal:
 
-    >>> print extract_text(
-    ...     find_tag_by_id(user_browser.contents, 'series-goal'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(user_browser.contents, 'series-goal')))
     Series goal: Proposed for hoary
 
 If the registration is performed by a user with permission to accept goals
@@ -358,10 +358,10 @@ for the series, the new blueprint is automatically accepted as a series goal:
     >>> control('Summary').value = summary
     >>> control('series goal').control.value = True
     >>> control('Register Blueprint').click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Network Magic: Auto Network Detection...
-    >>> print extract_text(
-    ...     find_tag_by_id(admin_browser.contents, 'series-goal'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(admin_browser.contents, 'series-goal')))
     Series goal: Accepted for hoary
 
 
@@ -391,7 +391,7 @@ Mozilla Firefox product, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/firefox/+spec/svg-support-1'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     SVG Support...
 
 
@@ -423,12 +423,12 @@ Mozilla Firefox product, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/firefox/+spec/svg-support-2'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     SVG Support...
 
 The new blueprint is not proposed as a series goal:
 
-    >>> print user_browser.getLink('Propose as goal')
+    >>> print(user_browser.getLink('Propose as goal'))
     <Link ...Propose as goal...
 
 Let's register another blueprint from the Mozilla Firefox 1.0 product series:
@@ -450,13 +450,13 @@ Mozilla Firefox product, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/firefox/+spec/svg-support-3'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     SVG Support...
 
 As requested, the new blueprint is proposed as a series goal:
 
     >>> content = find_main_content(user_browser.contents)
-    >>> print extract_text(find_tag_by_id(content, 'series-goal'))
+    >>> print(extract_text(find_tag_by_id(content, 'series-goal')))
     Series goal: Proposed for 1.0
 
 If the registration is performed by a user with permission to accept goals
@@ -472,9 +472,9 @@ for the series, the new blueprint is automatically accepted as a series goal:
     >>> control('series goal').control.value = True
     >>> control('Register Blueprint').click()
     >>> content = find_main_content(admin_browser.contents)
-    >>> print extract_text(content.h1)
+    >>> print(extract_text(content.h1))
     SVG Support...
-    >>> print extract_text(find_tag_by_id(content, 'series-goal'))
+    >>> print(extract_text(find_tag_by_id(content, 'series-goal')))
     Series goal: Accepted for 1.0
 
 
@@ -508,7 +508,7 @@ Mozilla Firefox project, then redirects the user to the new blueprint's page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/firefox/+spec/svg-support-5'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     SVG Support...
 
 
@@ -545,7 +545,7 @@ the user is then redirected back to the sprint page:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/sprints/futurista'
-    >>> print extract_text(find_main_content(user_browser.contents))
+    >>> print(extract_text(find_main_content(user_browser.contents)))
     Blueprints for Future Mega Meeting...
 
 In addition, the new blueprint is automatically proposed as a topic for
@@ -569,7 +569,7 @@ a sprint topic:
     >>> control('URL').value = 'http://bazaar-vcs.org/DarcsImports2'
     >>> control('Summary').value = summary
     >>> control('Register Blueprint').click()
-    >>> print extract_text(find_main_content(admin_browser.contents))
+    >>> print(extract_text(find_main_content(admin_browser.contents)))
     Blueprints for Future Mega Meeting...
     >>> print(find_tag_by_id(admin_browser.contents, 'speclisting'))
     <...darcs-imports-2...
@@ -609,7 +609,7 @@ permission, the blueprint will be automatically added to the sprint agenda:
     >>> sample_browser.getControl('Register Blueprint').click()
     >>> sample_browser.url
     'http://blueprints.launchpad.test/jokosher/+spec/spec-for-sprint'
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Spec for Sprint...
     >>> sample_browser.open('http://blueprints.launchpad.test/sprints/rome')
     >>> find_tag_by_id(sample_browser.contents, 'speclisting')
@@ -638,7 +638,7 @@ an error:
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
-    ...      print message.renderContents()
+    ...      print(message.renderContents())
     There is 1 error...already in use by another blueprint...
 
 Attempting to register a duplicate blueprint from a non-target context
@@ -653,10 +653,10 @@ produces the same error:
     >>> user_browser.getControl('Summary').value = (
     ...     'There is already a blueprint with this name')
     >>> user_browser.getControl('Register Blueprint').click()
-    >>> print user_browser.url
+    >>> print(user_browser.url)
     http://blueprints.launchpad.test/sprints/rome/+addspec
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
-    ...      print message.renderContents()
+    ...      print(message.renderContents())
     There is 1 error...already in use by another blueprint...
 
 
@@ -674,7 +674,7 @@ Blueprint names must conform to a set pattern:
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
-    ...      print message.renderContents()
+    ...      print(message.renderContents())
     There is 1 error...Invalid name...
 
 However, some invalid names can be transformed into valid names. When it is
@@ -690,7 +690,7 @@ automatically for the user:
     >>> control('Register Blueprint').click()
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+spec/new-network-magic'
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Network Magic: Automatic Network Detection...
 
 
@@ -709,7 +709,7 @@ blueprint:
     >>> user_browser.url
     'http://blueprints.launchpad.test/ubuntu/+addspec'
     >>> for message in find_tags_by_class(user_browser.contents, 'message'):
-    ...      print message.renderContents()
+    ...      print(message.renderContents())
     There is 1 error...is already registered by...
     ...Network Magic: Auto Network Detection...
 
@@ -722,7 +722,7 @@ a blueprint. To start with, it's not possible to register a blueprint from an
 individual user's blueprint listing page:
 
     >>> user_browser.open('http://blueprints.launchpad.test/~mark')
-    >>> print user_browser.getLink('Register a blueprint')
+    >>> print(user_browser.getLink('Register a blueprint'))
     Traceback (most recent call last):
         ...
     LinkNotFoundError...
@@ -731,7 +731,7 @@ It's also not possible to register a blueprint from a group's blueprint
 listing page:
 
     >>> user_browser.open('http://blueprints.launchpad.test/~admins')
-    >>> print user_browser.getLink('Register a blueprint')
+    >>> print(user_browser.getLink('Register a blueprint'))
     Traceback (most recent call last):
         ...
     LinkNotFoundError...
diff --git a/lib/lp/blueprints/stories/blueprints/xx-dependencies.txt b/lib/lp/blueprints/stories/blueprints/xx-dependencies.txt
index a35c373..1d446e6 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-dependencies.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-dependencies.txt
@@ -11,7 +11,7 @@ itself.
   >>> owner_browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
   >>> owner_browser.open(
   ...   'http://blueprints.launchpad.test/firefox/+spec/canvas')
-  >>> print find_main_content(owner_browser.contents)
+  >>> print(find_main_content(owner_browser.contents))
   <...
   ...Support E4X in EcmaScript...
   >>> 'Blocks' not in (owner_browser.contents)
@@ -202,7 +202,7 @@ implemented. The "dependency tree" page for "canvas" should show exactly
 that.
 
   >>> anon_browser.open('http://launchpad.test/firefox/+spec/canvas/+deptree')
-  >>> print '----'; print anon_browser.contents
+  >>> print('----'); print(anon_browser.contents)
   ----
   ...Blueprints that must be implemented first...
   ...Support E4X in EcmaScript...
@@ -218,7 +218,7 @@ image and map.
 
   >>> anon_browser.open(
   ...   'http://launchpad.test/firefox/+spec/canvas/+deptreeimgtag')
-  >>> print anon_browser.contents
+  >>> print(anon_browser.contents)
   <img src="deptree.png" usemap="#deptree" />
   <map id="deptree" name="deptree">
   <area shape="poly"
@@ -236,7 +236,7 @@ Get the dependency chart, and check that it is a PNG.
 
   >>> anon_browser.open(
   ...   'http://launchpad.test/firefox/+spec/canvas/deptree.png')
-  >>> anon_browser.contents.startswith('\x89PNG')
+  >>> anon_browser.contents.startswith(b'\x89PNG')
   True
   >>> anon_browser.headers['content-type']
   'image/png'
@@ -248,7 +248,7 @@ is useful for experimenting with the dot layout using production data.
   ...   'http://launchpad.test/firefox/+spec/canvas/+deptreedotfile')
   >>> anon_browser.headers['content-type']
   'text/plain;charset=utf-8'
-  >>> print anon_browser.contents
+  >>> print(anon_browser.contents)
   digraph "deptree" {
   ...
 
@@ -263,7 +263,7 @@ We create two blueprints in `ubuntu`.
   >>> owner_browser.getControl('Summary').value = (
   ...     'This is a blueprint for the Ubuntu distribution')
   >>> owner_browser.getControl('Register Blueprint').click()
-  >>> print owner_browser.url
+  >>> print(owner_browser.url)
   http://blueprints.launchpad.test/ubuntu/+spec/distro-blueprint-a
 
   >>> owner_browser.open('http://blueprints.launchpad.test/ubuntu/+addspec')
@@ -273,11 +273,11 @@ We create two blueprints in `ubuntu`.
   >>> owner_browser.getControl('Summary').value = (
   ...     'This is a blueprint for the Ubuntu distribution')
   >>> owner_browser.getControl('Register Blueprint').click()
-  >>> print owner_browser.url
+  >>> print(owner_browser.url)
   http://blueprints.launchpad.test/ubuntu/+spec/distro-blueprint-b
 
   >>> owner_browser.getLink('Add dependency').click()
-  >>> print owner_browser.url
+  >>> print(owner_browser.url)
   http.../ubuntu/+spec/distro-blueprint-b/+linkdependency
   
   >>> owner_browser.getControl('Depends On').value = 'distro-blueprint-a'
diff --git a/lib/lp/blueprints/stories/blueprints/xx-editing.txt b/lib/lp/blueprints/stories/blueprints/xx-editing.txt
index 2226ffc..54dfdfd 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-editing.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-editing.txt
@@ -26,8 +26,9 @@ Launchpad won't let us use an URL already used in another blueprint.
     >>> browser.getControl('Specification URL').value = url
     >>> browser.getControl('Change').click()
 
-    >>> message = ('https://wiki.ubuntu.com/MediaIntegrityCheck is already '
-    ...            'registered by')
+    >>> message = six.ensure_str(
+    ...     'https://wiki.ubuntu.com/MediaIntegrityCheck is already '
+    ...     'registered by')
     >>> message in browser.contents
     True
 
@@ -39,7 +40,7 @@ product.
     >>> browser.getControl('Specification URL').value = url
     >>> browser.getControl('Change').click()
 
-    >>> message = 'e4x is already in use by another blueprint'
+    >>> message = six.ensure_str('e4x is already in use by another blueprint')
     >>> message in browser.contents
     True
 
diff --git a/lib/lp/blueprints/stories/blueprints/xx-milestones.txt b/lib/lp/blueprints/stories/blueprints/xx-milestones.txt
index 98d68bd..02ff016 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-milestones.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-milestones.txt
@@ -6,7 +6,7 @@ the milestone page lists one feature targeted already, and no bugs:
 
     >>> admin_browser.open('http://launchpad.test/firefox/+milestone/1.0')
     >>> tag = find_tag_by_id(admin_browser.contents, 'specification-count')
-    >>> print extract_text(tag)
+    >>> print(extract_text(tag))
     1 blueprint
 
 We'll target the "canvas" blueprint. Each blueprint has a separate page for
@@ -15,7 +15,7 @@ milestone targeting.
   >>> admin_browser.open(
   ...     'http://blueprints.launchpad.test/firefox/+spec/canvas')
   >>> admin_browser.getLink('Target milestone').click()
-  >>> print admin_browser.title
+  >>> print(admin_browser.title)
   Target to a milestone : Support <canvas> Objects :
   Blueprints : Mozilla Firefox
   >>> back_link = admin_browser.getLink('Support <canvas> Objects')
@@ -42,16 +42,16 @@ milestone.
   <...Milestone target:...
   <.../firefox/+milestone/1.0...
 
-  >>> print admin_browser.getLink('1.0').url
+  >>> print(admin_browser.getLink('1.0').url)
   http://launchpad.test/firefox/+milestone/1.0
 
   >>> admin_browser.getLink('1.0').click()
-  >>> print admin_browser.getLink('Support <canvas> Objects').url
+  >>> print(admin_browser.getLink('Support <canvas> Objects').url)
   http://blueprints.launchpad.test/firefox/+spec/canvas
 
 The count of targeted features has also updated.
 
     >>> content = find_main_content(admin_browser.contents)
     >>> tag = find_tag_by_id(admin_browser.contents, 'specification-count')
-    >>> print extract_text(tag)
+    >>> print(extract_text(tag))
     2 blueprints
diff --git a/lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt b/lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt
index 045d33a..f9127fa 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-non-ascii-imagemap.txt
@@ -4,7 +4,7 @@ Non-ascii characters in specification titles are allowed.
   ...     'http://blueprints.launchpad.test/firefox/+spec/e4x/+edit')
 
   >>> admin_browser.getControl(
-  ...     'Title').value = 'A title with non-ascii characters áã'
+  ...     'Title').value = 'A title with non-ascii characters \xe1\xe3'
   >>> admin_browser.getControl('Change').click()
   >>> admin_browser.url
   'http://blueprints.launchpad.test/firefox/+spec/e4x'
@@ -12,7 +12,7 @@ Non-ascii characters in specification titles are allowed.
 And they're correctly displayed in the dependency graph imagemap.
 
   >>> anon_browser.open('http://launchpad.test/firefox/+spec/canvas/+deptreeimgtag')
-  >>> print anon_browser.contents
+  >>> print(anon_browser.contents)
   <img ...
   <map id="deptree" name="deptree">
   <area shape="poly" ...title="Support &lt;canvas&gt; Objects" .../>
diff --git a/lib/lp/blueprints/stories/blueprints/xx-productseries.txt b/lib/lp/blueprints/stories/blueprints/xx-productseries.txt
index 9ab9a6c..0de589c 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-productseries.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-productseries.txt
@@ -33,7 +33,7 @@ First we load the page to target to the series.
 
 We can see two potential series candidates, the "trunk" and the "1.0" series.
 
-    >>> print find_main_content(browser.contents)
+    >>> print(find_main_content(browser.contents))
     <...
     <option selected="selected" value="">(nothing selected)</option>
     <option value="2">firefox 1.0</option>
@@ -44,7 +44,7 @@ Now, we POST the form and expect to be redirected to the spec home page.
 Note that we use a user who DOES NOT have the "driver" role on that series,
 so the targeting should NOT be automatically approved.
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... POST /firefox/+spec/svg-support/+setproductseries HTTP/1.1
   ... Authorization: Basic celso.providelo@xxxxxxxxxxxxx:test
   ... Referer: https://launchpad.test/
@@ -67,7 +67,7 @@ so the targeting should NOT be automatically approved.
   ...
   ... Continue
   ... -----------------------------26999413214087432371486976730--
-  ... """)
+  ... """))
   HTTP/1.1 303 See Other
   ...
   Content-Length: 0
@@ -79,9 +79,9 @@ so the targeting should NOT be automatically approved.
 When we view that page, we see the targeted product series listed in the
 header.
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... GET /firefox/+spec/svg-support HTTP/1.1
-  ... """)
+  ... """))
   HTTP/1.1 200 Ok
   ...Proposed...
   ...firefox/1.0...
@@ -89,7 +89,7 @@ header.
 
 OK, we will also pitch the e4x spec to the same series:
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... POST /firefox/+spec/e4x/+setproductseries HTTP/1.1
   ... Authorization: Basic celso.providelo@xxxxxxxxxxxxx:test
   ... Referer: https://launchpad.test/
@@ -112,7 +112,7 @@ OK, we will also pitch the e4x spec to the same series:
   ...
   ... Continue
   ... -----------------------------26999413214087432371486976730--
-  ... """)
+  ... """))
   HTTP/1.1 303 See Other
   ...
   Content-Length: 0
@@ -123,10 +123,10 @@ OK, we will also pitch the e4x spec to the same series:
 
 And now both should show up on the "+setgoals" page for that product series.
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... GET /firefox/1.0/+setgoals HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
-  ... """)
+  ... """))
   HTTP/1.1 200 Ok
   ...Support Native SVG Objects...
   ...Support E4X in EcmaScript...
@@ -153,14 +153,15 @@ there are none left in the queue.
     ...     'http://blueprints.launchpad.test/firefox/1.0/+setgoals')
     >>> driver_browser.getControl('Support E4X').selected = True
     >>> driver_browser.getControl('Decline').click()
-    >>> 'Declined 1 specification(s)' in driver_browser.contents
+    >>> six.ensure_str('Declined 1 specification(s)') in (
+    ...     driver_browser.contents)
     True
 
 The accepted item should show up in the list of specs for this series:
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... GET /firefox/1.0/+specs HTTP/1.1
-  ... """)
+  ... """))
   HTTP/1.1 200 Ok
   ...Support Native SVG Objects...
 
@@ -168,9 +169,9 @@ The accepted item should show up in the list of specs for this series:
 As a final check, we will show that there is that spec in the "Deferred"
 listing.
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... GET /firefox/1.0/+specs?acceptance=declined HTTP/1.1
-  ... """)
+  ... """))
   HTTP/1.1 200 Ok
   ...Support E4X in EcmaScript...
 
@@ -179,7 +180,7 @@ Now, lets make sure that automatic approval works. We will move the accepted
 spec to the "trunk" series, where it will be automatically approved
 because we are an admin, then we will move it back.
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... POST /firefox/+spec/svg-support/+setproductseries HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
   ... Referer: https://launchpad.test/
@@ -202,7 +203,7 @@ because we are an admin, then we will move it back.
   ...
   ... Continue
   ... -----------------------------26999413214087432371486976730--
-  ... """)
+  ... """))
   HTTP/1.1 303 See Other
   ...
   Content-Length: 0
@@ -215,14 +216,14 @@ OK, lets see if it was immediately accepted:
 
     >>> anon_browser.open(
     ...     'http://launchpad.test/firefox/+spec/svg-support')
-    >>> 'firefox/trunk' in anon_browser.contents
+    >>> six.ensure_str('firefox/trunk') in anon_browser.contents
     True
-    >>> 'Accepted' in anon_browser.contents
+    >>> six.ensure_str('Accepted') in anon_browser.contents
     True
 
 And lets put it back:
 
-  >>> print http(r"""
+  >>> print(http(r"""
   ... POST /firefox/+spec/svg-support/+setproductseries HTTP/1.1
   ... Authorization: Basic Zm9vLmJhckBjYW5vbmljYWwuY29tOnRlc3Q=
   ... Referer: https://launchpad.test/
@@ -245,7 +246,7 @@ And lets put it back:
   ...
   ... Continue
   ... -----------------------------26999413214087432371486976730--
-  ... """)
+  ... """))
   HTTP/1.1 303 See Other
   ...
   Content-Length: 0
@@ -257,7 +258,7 @@ And again, it should be accepted automatically.
 
     >>> anon_browser.open(
     ...     'http://launchpad.test/firefox/+spec/svg-support')
-    >>> 'firefox/1.0' in anon_browser.contents
+    >>> six.ensure_str('firefox/1.0') in anon_browser.contents
     True
-    >>> 'Accepted' in anon_browser.contents
+    >>> six.ensure_str('Accepted') in anon_browser.contents
     True
diff --git a/lib/lp/blueprints/stories/blueprints/xx-superseding.txt b/lib/lp/blueprints/stories/blueprints/xx-superseding.txt
index 8433486..9e4910a 100644
--- a/lib/lp/blueprints/stories/blueprints/xx-superseding.txt
+++ b/lib/lp/blueprints/stories/blueprints/xx-superseding.txt
@@ -12,7 +12,7 @@ not already superseded).
     >>> browser.open(
     ...     'http://blueprints.launchpad.test/firefox/+spec/'
     ...     + 'extension-manager-upgrades')
-    >>> 'New' in browser.contents
+    >>> 'New' in six.ensure_text(browser.contents)
     True
 
 Make sure Bug 4116 stays fixed
@@ -39,9 +39,10 @@ Next, we will POST to that form, setting the spec which supersedes this one:
 Now, on the spec page we should see an alert that the spec has been
 superseded. The spec status should also have changed to superseded.
 
-    >>> 'This blueprint has been superseded.' in browser.contents
+    >>> 'This blueprint has been superseded.' in (
+    ...     six.ensure_text(browser.contents))
     True
-    >>> 'Superseded' in browser.contents
+    >>> 'Superseded' in six.ensure_text(browser.contents)
     True
 
 And finally, we want to clear the superseding spec data and reset the
@@ -56,5 +57,5 @@ then it should automatically do this:
 
 Let's confirm the status change:
 
-    >>> 'New' in browser.contents
+    >>> 'New' in six.ensure_text(browser.contents)
     True
diff --git a/lib/lp/blueprints/stories/sprints/sprint-settopics.txt b/lib/lp/blueprints/stories/sprints/sprint-settopics.txt
index 88531d4..dc4a125 100644
--- a/lib/lp/blueprints/stories/sprints/sprint-settopics.txt
+++ b/lib/lp/blueprints/stories/sprints/sprint-settopics.txt
@@ -55,7 +55,7 @@ First choose a driver for the UDS Guacamole sprint.
   'http://launchpad.test/sprints/uds-guacamole'
 
   >>> meeting_drivers = find_tag_by_id(browser.contents, 'meeting-drivers')
-  >>> print extract_text(meeting_drivers.findNext('a'))
+  >>> print(extract_text(meeting_drivers.findNext('a')))
   Ubuntu Team
 
 Any member of the Ubuntu-Team can now approve and/or decline items to the UDS 
@@ -68,7 +68,7 @@ Guacamole agenda.
   'http://blueprints.launchpad.test/sprints/uds-guacamole'
   >>> cprov_browser.getLink('Set agenda').click()
 
-  >>> print cprov_browser.title
+  >>> print(cprov_browser.title)
   Review discussion topics for “Ubuntu DevSummit Guacamole” sprint :
   Blueprints :
   Ubuntu DevSummit Guacamole :
diff --git a/lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt b/lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt
index 72969d8..f9e2b34 100644
--- a/lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt
+++ b/lib/lp/blueprints/stories/sprints/xx-sprint-meeting-export.txt
@@ -10,7 +10,7 @@ Add some attendees to the sprint.
 Get the sprint meeting export:
 
     >>> browser.open('http://launchpad.test/sprints/ubz/+temp-meeting-export')
-    >>> print browser.headers['content-type']
+    >>> print(browser.headers['content-type'])
     application/xml;charset=utf-8
 
 
@@ -25,7 +25,7 @@ element name:
 
     >>> from xml.dom.minidom import parseString
     >>> document = parseString(str(browser.contents))
-    >>> print document.documentElement.nodeName
+    >>> print(document.documentElement.nodeName)
     schedule
 
 The attendees element contains a list of person elements.
@@ -35,7 +35,7 @@ The attendees element contains a list of person elements.
     >>> soup = BeautifulSoup(browser.contents, 'xml')
     >>> people = soup.find('attendees').findAll('person')
     >>> for person in sorted(people, key=operator.itemgetter("displayname")):
-    ...     print "%(displayname)s, %(name)s, %(start)s -> %(end)s" % person
+    ...     print("%(displayname)s, %(name)s, %(start)s -> %(end)s" % person)
     Celso Providelo, cprov, 2005-10-07T23:30:00Z -> 2005-11-17T00:11:00Z
     Foo Bar, name16, 2005-10-07T23:30:00Z -> 2005-11-17T00:11:00Z
 
@@ -45,6 +45,6 @@ actually refers to a Specification.
     >>> soup = BeautifulSoup(browser.contents, 'xml')
     >>> meetings = soup.find('unscheduled').findAll('meeting')
     >>> for meeting in meetings:
-    ...     print "%(id)s: %(name)s, %(lpurl)s" % meeting
+    ...     print("%(id)s: %(name)s, %(lpurl)s" % meeting)
     3: svg-support, .../+spec/svg-support
     1: extension-manager-upgrades, .../+spec/extension-manager-upgrades
diff --git a/lib/lp/blueprints/stories/sprints/xx-sprints.txt b/lib/lp/blueprints/stories/sprints/xx-sprints.txt
index 57bc6b3..2b26b63 100644
--- a/lib/lp/blueprints/stories/sprints/xx-sprints.txt
+++ b/lib/lp/blueprints/stories/sprints/xx-sprints.txt
@@ -26,7 +26,7 @@ Let's start by viewing the list of sprints registered.
     >>> user_browser.title
     'Meetings and sprints registered in Launchpad'
 
-    >>> print find_tag_by_id(user_browser.contents, 'application-summary')
+    >>> print(find_tag_by_id(user_browser.contents, 'application-summary'))
     <p ...
       Launchpad can help you organize your developer sprints, summits and
       gatherings. Register the meeting here, then you can invite people to
@@ -52,7 +52,7 @@ Sprints +new page.
     >>> user_browser.open('http://launchpad.test/sprints')
     >>> user_browser.getLink('Register a meeting').click()
 
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Register a meeting...
 
 First we'll test the name field validator.
@@ -71,7 +71,7 @@ First we'll test the name field validator.
     >>> user_browser.getControl('Add Sprint').click()
 
     >>> for tag in find_tags_by_class(user_browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There is 1 error.
     <BLANKLINE>
     Invalid name 'ltsp_on_steroids!'. Names must be at least two characte...
@@ -83,7 +83,7 @@ nice error message
     >>> user_browser.getControl('Add Sprint').click()
 
     >>> for tag in find_tags_by_class(user_browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There is 1 error.
     <BLANKLINE>
     ubz is already in use by another sprint.
@@ -99,7 +99,7 @@ a error message.
     >>> user_browser.getControl('Add Sprint').click()
 
     >>> for tag in find_tags_by_class(user_browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There is 1 error.
     <BLANKLINE>
     This event can't start after it ends
@@ -131,7 +131,7 @@ the new sprint.
 Since the sprint's time zone was set to UTC, the dates are displayed in
 that time zone:
 
-    >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents, 'start-end')))
     Starts: 09:15 UTC on Tuesday, 2006-10-10
     Ends: 16:00 UTC on Friday, 2006-10-13
 
@@ -160,7 +160,7 @@ Add a new sprint with a different time zone is also handled correctly.
     >>> user_browser.url
     'http://launchpad.test/sprints/africa-sprint'
 
-    >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents, 'start-end')))
     Starts: 09:15 SAST on Monday, 2006-07-10
     Ends: 16:00 SAST on Thursday, 2006-07-13
 
@@ -170,7 +170,7 @@ We should be able to edit the details on a sprint but the menus are only
 available to those who have permissions to edit that sprint.
 
     >>> anon_browser.open('http://launchpad.test/sprints/ubz')
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Ubuntu Below Zero : Meetings
 
     >>> anon_browser.getLink('Change details')
@@ -183,7 +183,7 @@ We will log in as Sample Person and edit the ubz sprint.
     >>> browser.addHeader('Authorization', 'Basic test@xxxxxxxxxxxxx:test')
 
     >>> browser.open('http://launchpad.test/sprints/ubz')
-    >>> print browser.title
+    >>> print(browser.title)
     Ubuntu Below Zero : Meetings
 
     >>> address = 'Holiday Inn Select, Downtown Montreal, Canada'
@@ -213,7 +213,7 @@ should receive a nice error message.
     >>> browser.getControl('Change').click()
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There is 1 error.
     This event can't start after it ends
 
@@ -234,10 +234,10 @@ sprint home page.
 
 The address of the sprint is now visible.
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'sprint-address'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'sprint-address')))
     Address: Holiday Inn Select, Downtown Montreal, Canada
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'start-end'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'start-end')))
     Starts: 08:30 EST on Tuesday, 2006-01-10
     Ends: 17:00 EST on Sunday, 2006-02-12
 
@@ -248,10 +248,10 @@ dates will be changed too, since they follow local time:
     >>> browser.open('http://launchpad.test/sprints/ubz/+edit')
     >>> browser.getControl('Timezone').value = ['Australia/Darwin']
     >>> browser.getControl('Change').click()
-    >>> print browser.url
+    >>> print(browser.url)
     http://launchpad.test/sprints/ubz
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'start-end'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'start-end')))
     Starts: 08:30 ACST on Tuesday, 2006-01-10
     Ends: 17:00 ACST on Sunday, 2006-02-12
 
@@ -259,14 +259,14 @@ dates will be changed too, since they follow local time:
 We should be able to see the workload of a sprint:
 
     >>> anon_browser.open('http://launchpad.test/sprints/ubz/+assignments')
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Assignments : Blueprints : Ubuntu Below Zero : Meetings
 
 We should be able to see the spec assignment table of a sprint:
 
     >>> mainarea = find_main_content(anon_browser.contents)
     >>> for header in mainarea.findAll('th'):
-    ...     print header.renderContents()
+    ...     print(header.renderContents())
     Priority
     Name
     Definition
@@ -281,7 +281,7 @@ no spec assigned to people.
     >>> anon_browser.open(
     ...     'http://launchpad.test/sprints/ltsponsteroids/+assignments')
     >>> notice = find_tag_by_id(anon_browser.contents, 'no-blueprints')
-    >>> print extract_text(notice)
+    >>> print(extract_text(notice))
     There are no open blueprints.
 
 
@@ -298,7 +298,7 @@ It should be possible to register yourself to attend the sprint:
     >>> browser.url
     'http://launchpad.test/sprints/ubz/+attend'
 
-    >>> print browser.title
+    >>> print(browser.title)
     Register your attendance : Ubuntu Below Zero : Meetings
 
 Invalid dates, for instance entering a starting date after the ending
@@ -326,11 +326,11 @@ date up to one day after the sprint ends.
     >>> browser.getControl('To').value = '2005-02-04 20:11:00'
     >>> browser.getControl('Register').click()
 
-    >>> print browser.url
+    >>> print(browser.url)
     http://launchpad.test/sprints/ubz/+attend
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There is 1 error.
     Please pick a date after 2006-01-10 08:30
 
@@ -340,11 +340,11 @@ An attendance that starts after the end of the sprint is also an error:
     >>> browser.getControl('To').value = '2010-07-10 22:11:00'
     >>> browser.getControl('Register').click()
 
-    >>> print browser.url
+    >>> print(browser.url)
     http://launchpad.test/sprints/ubz/+attend
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There are 2 errors.
     Please pick a date before 2006-02-12 17:00
     Please pick a date before 2006-02-13 17:00
@@ -356,11 +356,11 @@ error:
     >>> browser.getControl('To').value = '1990-07-10 22:11:00'
     >>> browser.getControl('Register').click()
 
-    >>> print browser.url
+    >>> print(browser.url)
     http://launchpad.test/sprints/ubz/+attend
 
     >>> for tag in find_tags_by_class(browser.contents, 'message'):
-    ...     print tag.renderContents()
+    ...     print(tag.renderContents())
     There are 2 errors.
     Please pick a date after 2006-01-09 08:30
     Please pick a date after 2006-01-10 08:30
@@ -381,7 +381,7 @@ Now, Sample Person should be listed as an attendee.
     ...     """Print the attendees listed in the attendees portlet."""
     ...     attendees_portlet = find_portlet(sprint_page, 'Attendees')
     ...     for li in attendees_portlet.findAll('ul')[0].findAll('li'):
-    ...         print li.a.string.encode('ascii', 'xmlcharrefreplace')
+    ...         print(li.a.string.encode('ascii', 'xmlcharrefreplace'))
 
     >>> print_attendees(browser.contents)
     Sample Person
@@ -390,10 +390,10 @@ If we return to the "Register Yourself" form, the previously entered
 dates are prefilled (they have been clamped to the sprint duration):
 
     >>> browser.getLink('Register yourself').click()
-    >>> print browser.getControl('From').value
+    >>> print(browser.getControl('From').value)
     2006-01-10 10:30
 
-    >>> print browser.getControl('To').value
+    >>> print(browser.getControl('To').value)
     2006-02-12 17:00
 
 Also, it is possible to register someone else. Let's register Carlos.
@@ -468,18 +468,18 @@ First, we add a couple of IRC nicknames for Carlos.
     >>> logout()
 
     >>> browser.getLink('Export attendees to CSV').click()
-    >>> print browser.headers['content-type']
+    >>> print(browser.headers['content-type'])
     text/csv
 
     >>> carlos_browser = setupBrowser(auth='Basic carlos@xxxxxxxxxxxxx:test')
     >>> carlos_browser.open('http://launchpad.test/sprints/ubz')
     >>> carlos_browser.getLink('Export attendees to CSV').click()
-    >>> print carlos_browser.headers['content-type']
+    >>> print(carlos_browser.headers['content-type'])
     text/csv
 
     >>> admin_browser.open('http://launchpad.test/sprints/ubz')
     >>> admin_browser.getLink('Export attendees to CSV').click()
-    >>> print admin_browser.headers['content-type']
+    >>> print(admin_browser.headers['content-type'])
     text/csv
 
 The resulting CSV file lists physical attendance correctly.
diff --git a/lib/lp/blueprints/stories/standalone/subscribing.txt b/lib/lp/blueprints/stories/standalone/subscribing.txt
index 678f3b0..c372a22 100644
--- a/lib/lp/blueprints/stories/standalone/subscribing.txt
+++ b/lib/lp/blueprints/stories/standalone/subscribing.txt
@@ -28,7 +28,7 @@ Carlos.
     >>> browser.addHeader('Authorization', 'Basic carlos@xxxxxxxxxxxxx:test')
     >>> browser.open(
     ...     "http://blueprints.launchpad.test/firefox/+spec/e4x/+subscribe";)
-    >>> print browser.title
+    >>> print(browser.title)
     Subscribe to blueprint : Support E4X in EcmaScript :
     Blueprints : Mozilla Firefox
 
@@ -52,10 +52,11 @@ Now, we'll POST the form. We should see a message that we have just
 subscribed.
 
     >>> browser.getControl('Subscribe').click()
-    >>> 'You have subscribed to this blueprint' in browser.contents
+    >>> six.ensure_str('You have subscribed to this blueprint') in (
+    ...     browser.contents)
     True
 
-    >>> 'subscriber-essential' in browser.contents
+    >>> six.ensure_str('subscriber-essential') in browser.contents
     True
 
 Now the link should say "Update subscription" in the actions menu.
@@ -76,10 +77,11 @@ We will unset the essential flag and resubmit:
 
     >>> essential.selected = False
     >>> browser.getControl('Change').click()
-    >>> 'Your subscription has been updated' in browser.contents
+    >>> six.ensure_str('Your subscription has been updated') in (
+    ...     browser.contents)
     True
 
-    >>> 'subscriber-inessential' in browser.contents
+    >>> six.ensure_str('subscriber-inessential') in browser.contents
     True
 
 It's also possible to change the essential flag clicking on the star
@@ -91,7 +93,7 @@ icon in the Subscribers portlet.
     >>> browser.url
     'http://blueprints.launchpad.test/firefox/+spec/e4x'
 
-    >>> 'subscriber-essential' in browser.contents
+    >>> six.ensure_str('subscriber-essential') in browser.contents
     True
 
 We don't really want to be subscribed, so lets unsubscribe from that
@@ -102,13 +104,14 @@ unsubscribe confirmation page loads.
     >>> unsubit.click()
     >>> confirm = browser.getControl('Unsubscribe')
     >>> confirm.click()
-    >>> 'You have unsubscribed from this blueprint.' in browser.contents
+    >>> six.ensure_str('You have unsubscribed from this blueprint.') in (
+    ...     browser.contents)
     True
 
-    >>> 'subscriber-inessential' in browser.contents
+    >>> six.ensure_str('subscriber-inessential') in browser.contents
     False
 
-    >>> 'subscriber-essential' in browser.contents
+    >>> six.ensure_str('subscriber-essential') in browser.contents
     False
 
 
@@ -125,7 +128,8 @@ When we want other users to track a specification we can subscribe them.
 
     >>> browser.getControl('Subscriber').value = 'stub'
     >>> browser.getControl('Subscribe').click()
-    >>> msg = "Stuart Bishop has been subscribed to this blueprint."
+    >>> msg = six.ensure_str(
+    ...     "Stuart Bishop has been subscribed to this blueprint.")
     >>> msg in browser.contents
     True
 
@@ -163,8 +167,8 @@ based on the subscription change we have made above.
     >>> for subscriber in subscribers:
     ...     a_tags = subscriber.findAll('a')
     ...     img = a_tags[0].find('img')
-    ...     print img['src'],
-    ...     print a_tags[1].string
+    ...     print(img['src'], end=' ')
+    ...     print(a_tags[1].string)
     /@@/subscriber-essential Stuart Bishop
 
 When we change a user's subscription, they get notified by email. Teams
@@ -188,8 +192,8 @@ based on the subscription change we have made above.
     >>> for subscriber in subscribers:
     ...     a_tags = subscriber.findAll('a')
     ...     img = a_tags[0].find('img')
-    ...     print img['src'],
-    ...     print a_tags[1].string
+    ...     print(img['src'], end=' ')
+    ...     print(a_tags[1].string)
     /@@/subscriber-inessential Stuart Bishop
 
 And check the email notification too.
@@ -235,7 +239,7 @@ does not have a preferred email address, an email is sent to each active
 member who has a preferred email registered.
 
     >>> login('admin@xxxxxxxxxxxxx')
-    >>> print admins.preferredemail
+    >>> print(admins.preferredemail)
     None
 
     >>> admins_contact_email_addresses == sorted(
@@ -259,7 +263,7 @@ is sent to each active member.
 
 The Ubuntu Team does have a preferred email address.
 
-    >>> print ubuntu_team.preferredemail
+    >>> print(ubuntu_team.preferredemail)
     <...EmailAddress...>
 
     >>> logout()
@@ -276,7 +280,7 @@ list, and now the unsubscribe confirmation page loads.
     >>> unsubit.click()
     >>> confirm = browser.getControl('Unsubscribe')
     >>> confirm.click()
-    >>> msg = (
+    >>> msg = six.ensure_str(
     ...     "Launchpad Administrators has been unsubscribed from this "
     ...     "blueprint.")
     >>> msg in browser.contents
@@ -321,8 +325,8 @@ not.
     >>> for subscriber in subscribers:
     ...     a_tags = subscriber.findAll('a')
     ...     img = a_tags[0].find('img')
-    ...     print img['src'],
-    ...     print a_tags[1].string
+    ...     print(img['src'], end=' ')
+    ...     print(a_tags[1].string)
     /@@/subscriber-essential Andrew Bennetts
     /@@/subscriber-inessential Dafydd Harries
     /@@/subscriber-inessential Foo Bar
diff --git a/lib/lp/blueprints/stories/standalone/xx-batching.txt b/lib/lp/blueprints/stories/standalone/xx-batching.txt
index 6603874..0c3ca4c 100644
--- a/lib/lp/blueprints/stories/standalone/xx-batching.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-batching.txt
@@ -26,7 +26,7 @@ To demonstrate this, we'll create a new project:
 In the beginning, a project hasn't had blueprints set up:
 
   >>> browser.open("http://blueprints.launchpad.test/big-project";)
-  >>> print extract_text(find_main_content(browser.contents))
+  >>> print(extract_text(find_main_content(browser.contents)))
   Blueprints...does not know how...Configure Blueprints...
 
 But it's easy to change that.
@@ -40,7 +40,7 @@ But it's easy to change that.
 Initially the newly enabled feature has no blueprints.
 
   >>> browser.open("http://blueprints.launchpad.test/big-project";)
-  >>> print extract_text(find_main_content(browser.contents))
+  >>> print(extract_text(find_main_content(browser.contents)))
   Blueprints...first blueprint in this project!...
   
 We'll go ahead and add just a single blueprint:
@@ -57,8 +57,8 @@ When we ask for the complete list of blueprints for our project, the new
 blueprint is listed:
 
   >>> browser.open("http://blueprints.launchpad.test/big-project";)
-  >>> print extract_text(first_tag_by_class(browser.contents, 
-  ...                                      'batch-navigation-index'))
+  >>> print(extract_text(first_tag_by_class(browser.contents, 
+  ...                                      'batch-navigation-index')))
   1...→...1...of...1 result
   
 Let's add some more blueprints:
@@ -74,20 +74,20 @@ Observe that now when we ask for the complete list of blueprints, only some of
 the blueprints are listed:
 
   >>> browser.open("http://blueprints.launchpad.test/big-project";)
-  >>> print extract_text(first_tag_by_class(browser.contents, 
-  ...                                      'batch-navigation-index'))
+  >>> print(extract_text(first_tag_by_class(browser.contents, 
+  ...                                      'batch-navigation-index')))
   1...→...5...of...20 results
 
 We can go to the next batch of blueprints by following the 'Next' link:
     
   >>> browser.getLink('Next').click()
-  >>> print extract_text(first_tag_by_class(browser.contents, 
-  ...                                      'batch-navigation-index'))
+  >>> print(extract_text(first_tag_by_class(browser.contents, 
+  ...                                      'batch-navigation-index')))
   6...→...10...of...20 results
 
 Following the 'Last' link takes us to the last batch of blueprints:
 
   >>> browser.getLink('Last').click()
-  >>> print extract_text(first_tag_by_class(browser.contents, 
-  ...                                      'batch-navigation-index'))
+  >>> print(extract_text(first_tag_by_class(browser.contents, 
+  ...                                      'batch-navigation-index')))
   16...→...20...of...20 results
diff --git a/lib/lp/blueprints/stories/standalone/xx-branch-links.txt b/lib/lp/blueprints/stories/standalone/xx-branch-links.txt
index 8a58af9..b7a37bf 100644
--- a/lib/lp/blueprints/stories/standalone/xx-branch-links.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-branch-links.txt
@@ -26,10 +26,10 @@ If the user is not logged in, they will be asked to log in.
     >>> def printSpecBranchLinks(browser):
     ...     tags = find_tags_by_class(browser.contents, 'spec-branch-summary')
     ...     if len(tags) == 0:
-    ...         print 'No spec branch links'
+    ...         print('No spec branch links')
     ...     else:
     ...         for tag in tags:
-    ...             print extract_text(tag)
+    ...             print(extract_text(tag))
 
 When linking from a branch to a spec, a select widget is used as a
 method for selecting values.  This widget is populated with the
@@ -74,8 +74,8 @@ The blueprint page shows the linked branches in a portlet.
 
     >>> user_browser.open(
     ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support')
-    >>> print extract_text(find_tag_by_id(user_browser.contents,
-    ...     'linked_branches'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents,
+    ...     'linked_branches')))
     Related branches
     lp://dev/~mark/firefox/release-0.8
     Link to another branch
@@ -107,7 +107,7 @@ The branch is now no longer listed in the portlet:
 
     >>> user_browser.open(
     ...     'http://blueprints.launchpad.test/firefox/+spec/svg-support')
-    >>> print extract_text(find_tag_by_id(user_browser.contents,
-    ...     'linked_branches'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents,
+    ...     'linked_branches')))
     Related branches
     Link a related branch
diff --git a/lib/lp/blueprints/stories/standalone/xx-index.txt b/lib/lp/blueprints/stories/standalone/xx-index.txt
index fc6731e..9298a7b 100644
--- a/lib/lp/blueprints/stories/standalone/xx-index.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-index.txt
@@ -22,7 +22,7 @@ a single Launchpad project or across all projects.
 Users can see the latest blueprints available for the whole system:
 
     >>> user_browser.open('http://blueprints.launchpad.test/')
-    >>> print extract_text(find_main_content(user_browser.contents))
+    >>> print(extract_text(find_main_content(user_browser.contents)))
     Blueprints
     All projects...
     Recently registered
@@ -43,13 +43,13 @@ name and title, across all projects:
     >>> browser.getControl(name='field.search_text').value = 'svg'
     >>> browser.getControl(name='field.scope').value = ['all']
     >>> browser.getControl(name='field.actions.search').click()
-    >>> print browser.url
+    >>> print(browser.url)
     http://blueprints.launchpad.test/?searchtext=svg
 
 There is one such blueprint for the Firefox project, and it is listed
 among the results:
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'specs-table'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'specs-table')))
     Support Native SVG Objects
     ...
 
@@ -61,9 +61,9 @@ find the same result:
     >>> browser.getControl(name='field.scope').value = ['project']
     >>> browser.getControl(name='field.scope.target').value = 'firefox'
     >>> browser.getControl(name='field.actions.search').click()
-    >>> print browser.url
+    >>> print(browser.url)
     http://blueprints.launchpad.test/firefox?searchtext=svg
-    >>> print browser.getLink('svg-support').attrs['title']
+    >>> print(browser.getLink('svg-support').attrs['title'])
     Support Native SVG Objects
 
 The search form forces us to choose either searching within all projects
@@ -80,8 +80,8 @@ selecting the `all` scope:
     >>> browser.open('http://blueprints.launchpad.test/specs?'
     ...     'field.actions.search=Find+Blueprints&field.scope.target='
     ...     '&field.search_text=svg')
-    >>> print browser.url
+    >>> print(browser.url)
     http://blueprints.launchpad.test/?searchtext=svg
-    >>> print extract_text(find_tag_by_id(browser.contents, 'specs-table'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'specs-table')))
     Support Native SVG Objects
     ...
diff --git a/lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt b/lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt
index 03224d3..e2d67f8 100644
--- a/lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-informational-blueprints.txt
@@ -47,8 +47,8 @@ project's documentation page.
     >>> user_browser.getControl('Definition Status').value = ['APPROVED']
     >>> user_browser.getControl('Change').click()
     >>> browser.open('http://blueprints.launchpad.test/jokosher/+documentation')
-    >>> print extract_text(
-    ...     find_tag_by_id(browser.contents, 'documentation-listing-table'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(browser.contents, 'documentation-listing-table')))
     ...
     Informational blueprint
     for Jokosher
diff --git a/lib/lp/blueprints/stories/standalone/xx-overview.txt b/lib/lp/blueprints/stories/standalone/xx-overview.txt
index eb71b2d..51f6894 100644
--- a/lib/lp/blueprints/stories/standalone/xx-overview.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-overview.txt
@@ -36,7 +36,7 @@ of blueprints attached to Mozilla Firefox by following the "Blueprints" tab:
     >>> user_browser.open('http://launchpad.test/firefox')
     >>> user_browser.getLink('Blueprints').click()
     >>> main = find_main_content(user_browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Blueprints for Mozilla Firefox...
     Priority...Blueprint...Design  ...Delivery      ...
     High    ...svg-support  ...Approved...Beta Available...
@@ -52,7 +52,7 @@ the blueprints page for 1.0:
 
     >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> main = find_main_content(user_browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Blueprints for 1.0
     Launchpad lets projects track the features they intend to implement...
 
@@ -61,11 +61,11 @@ Let's target an existing Mozilla Firefox blueprint to the 1.0 series:
     >>> browser = admin_browser
     >>> browser.open('http://launchpad.test/firefox/+specs')
     >>> browser.getLink('svg-support').click()
-    >>> print browser.title
+    >>> print(browser.title)
     Support Native SVG Objects...
     >>> browser.getLink('Propose as goal').click()
     >>> main = find_main_content(browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Target to a product series
     Support Native SVG Objects
     ...
@@ -74,7 +74,7 @@ Let's target an existing Mozilla Firefox blueprint to the 1.0 series:
     >>> series.displayValue = ['firefox 1.0']
     >>> browser.getControl('Continue').click()
     >>> main = find_main_content(browser.contents)
-    >>> print extract_text(find_tag_by_id(main, 'series-goal'))
+    >>> print(extract_text(find_tag_by_id(main, 'series-goal')))
     Series goal: Accepted for 1.0...
 
 We'll also target the blueprint to a milestone.  First we'll create a
@@ -93,7 +93,7 @@ Now we'll target our chosen blueprint to the new milestone:
     >>> browser.getLink('svg-support').click()
     >>> browser.getLink('Target milestone').click()
     >>> main = find_main_content(browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Target to a milestone
     ...
     Support Native SVG Objects
@@ -104,7 +104,7 @@ Now we'll target our chosen blueprint to the new milestone:
     >>> milestones.displayValue = ['Mozilla Firefox 1.0.9']
     >>> browser.getControl('Change').click()
     >>> main = find_main_content(browser.contents)
-    >>> print extract_text(find_tag_by_id(main, 'milestone-target'))
+    >>> print(extract_text(find_tag_by_id(main, 'milestone-target')))
     Milestone target:...1.0.9...
 
 Now the blueprint listing for the 1.0 series includes an entry for our chosen
@@ -112,7 +112,7 @@ blueprint. It also lists the milestone to which the blueprint is targeted:
 
     >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> main = find_main_content(user_browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Blueprints for 1.0...
     Priority...Blueprint...Design  ...Delivery...Assignee...Milestone...
     High    ...svg-support  ...Approved...Beta    ...Carlos  ...1.0.9    ...
@@ -120,7 +120,7 @@ blueprint. It also lists the milestone to which the blueprint is targeted:
 It's possible to navigate to the milestone directly:
 
     >>> user_browser.getLink('1.0.9').click()
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     1.0.9 : Mozilla Firefox
 
 
@@ -133,7 +133,7 @@ blueprints attached to Ubuntu Linux by following the "Blueprints" tab:
     >>> user_browser.open('http://launchpad.test/ubuntu')
     >>> user_browser.getLink('Blueprints').click()
     >>> main = find_main_content(user_browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Blueprints for Ubuntu...
     Priority ...Blueprint        ...Design    ...Delivery...
     Undefined...media-integrity-check...Discussion...Unknown...
@@ -149,7 +149,7 @@ blueprints page for Grumpy:
 
     >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
     >>> main = find_main_content(user_browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Blueprints for Grumpy
     Launchpad lets projects track the features they intend to implement...
 
@@ -159,11 +159,11 @@ Let's target an existing Ubuntu blueprint to the Grumpy series:
     >>> browser.open('http://launchpad.test/ubuntu/+specs')
     >>> browser.getLink('media-integrity-check').click()
     >>> main = find_main_content(browser.contents)
-    >>> print browser.title
+    >>> print(browser.title)
     CD Media Integrity Check...
     >>> browser.getLink('Propose as goal').click()
     >>> main = find_main_content(browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Target to a distribution series
     CD Media Integrity Check
     ...
@@ -171,7 +171,7 @@ Let's target an existing Ubuntu blueprint to the Grumpy series:
     >>> series.displayValue = ['ubuntu grumpy']
     >>> browser.getControl('Continue').click()
     >>> main = find_main_content(browser.contents)
-    >>> print extract_text(find_tag_by_id(browser.contents, 'series-goal'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'series-goal')))
     Series goal: Accepted for grumpy...
 
 We'll also target the blueprint to a milestone.  First we'll create a
@@ -189,7 +189,7 @@ Now we'll target our chosen blueprint to the new milestone:
     >>> browser.open('http://launchpad.test/ubuntu/+specs')
     >>> browser.getLink('media-integrity-check').click()
     >>> browser.getLink('Target milestone').click()
-    >>> print extract_text(find_main_content(browser.contents))
+    >>> print(extract_text(find_main_content(browser.contents)))
     Target to a milestone
     ...
     CD Media Integrity Check
@@ -199,8 +199,8 @@ Now we'll target our chosen blueprint to the new milestone:
     >>> milestones = browser.getControl('Milestone')
     >>> milestones.displayValue = ['Ubuntu drift-1']
     >>> browser.getControl('Change').click()
-    >>> print extract_text(
-    ...     find_tag_by_id(browser.contents, 'milestone-target'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(browser.contents, 'milestone-target')))
     Milestone target: drift-1
 
 Finally, the blueprint listing for Grumpy includes an entry for our chosen
@@ -208,7 +208,7 @@ blueprint. It also lists the milestone to which the blueprint is targeted:
 
     >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
     >>> main = find_main_content(user_browser.contents)
-    >>> print extract_text(main).encode('ascii', 'backslashreplace')
+    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
     Blueprints for Grumpy...
     Priority ...Blueprint        ...Design    ...Delivery...Milestone...
     Undefined...media-integrity-check...Discussion...Unknown ...drift-1  ...
@@ -216,5 +216,5 @@ blueprint. It also lists the milestone to which the blueprint is targeted:
 It's possible to navigate to the milestone directly:
 
     >>> user_browser.getLink('drift-1').click()
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     drift-1 : Ubuntu
diff --git a/lib/lp/blueprints/stories/standalone/xx-personviews.txt b/lib/lp/blueprints/stories/standalone/xx-personviews.txt
index 81b41e0..c20ae7a 100644
--- a/lib/lp/blueprints/stories/standalone/xx-personviews.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-personviews.txt
@@ -8,7 +8,7 @@ The Features link shows an overview of all the specifications
 involving that person.
 
     >>> browser.open('http://blueprints.launchpad.test/~name16')
-    >>> print browser.title
+    >>> print(browser.title)
     Blueprints : Foo Bar
     >>> soup = find_main_content(browser.contents)
     >>> soup('h1')
@@ -66,7 +66,7 @@ in the workload of that person.
     >>> browser.getLink('Workload').click()
     >>> browser.url
     '.../~name16/+specworkload'
-    >>> print browser.title
+    >>> print(browser.title)
     Blueprint workload...
 
 For a team, the 'Workload' link first shows the specifications that
@@ -75,14 +75,14 @@ for each member of the team, using batching.
 
     >>> from lp.services.helpers import backslashreplace
     >>> browser.open('http://blueprints.launchpad.test/~admins')
-    >>> print backslashreplace(browser.title)
+    >>> print(backslashreplace(browser.title))
     Blueprints : \u201cLaunchpad Administrators\u201d team
     >>> browser.getLink('Workload').click()
     >>> browser.url
     '.../~admins/+specworkload'
-    >>> print browser.title
+    >>> print(browser.title)
     Blueprint workload...
-    >>> print extract_text(find_main_content(browser.contents))
+    >>> print(extract_text(find_main_content(browser.contents)))
     Blueprint workload...
     Team member workload...
     Andrew Bennetts's specifications:...
diff --git a/lib/lp/blueprints/stories/standalone/xx-retargeting.txt b/lib/lp/blueprints/stories/standalone/xx-retargeting.txt
index 526bb74..523cd5d 100644
--- a/lib/lp/blueprints/stories/standalone/xx-retargeting.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-retargeting.txt
@@ -59,7 +59,7 @@ We stay on the same page and get an error message printed out:
   'http://blueprints.launchpad.test/firefox/+spec/svg-support/+retarget'
 
   >>> for tag in find_tags_by_class(admin_browser.contents, 'message'):
-  ...     print tag.renderContents()
+  ...     print(tag.renderContents())
   There is 1 error.
   <BLANKLINE>
   There is no project with the name 'foo bar'. Please check that name and try again.
diff --git a/lib/lp/blueprints/stories/standalone/xx-views.txt b/lib/lp/blueprints/stories/standalone/xx-views.txt
index e331bb4..f37bcfc 100644
--- a/lib/lp/blueprints/stories/standalone/xx-views.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-views.txt
@@ -21,96 +21,96 @@ Viewing current blueprints
 We should be able to see a "+specs" and a "+portlet-latestspecs" view
 on sprint, product, person, project and distribution.
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /firefox/+portlet-latestspecs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /ubuntu/+portlet-latestspecs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /mozilla/+portlet-latestspecs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /~carlos/+portlet-latestspecs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /sprints/ubz/+portlet-latestspecs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /firefox/+specs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /ubuntu/+specs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /mozilla/+specs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /~carlos/+specs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /sprints/ubz/+specs HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
 The other way of sorting blueprints is by assignee:
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /firefox/+assignments HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /ubuntu/+assignments HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /mozilla/+assignments HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /~carlos/+assignments HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /sprints/ubz/+assignments HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
@@ -118,33 +118,33 @@ Also, we should have "+documentation" views on product, productseries,
 project, distro and distroseries.
 
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /firefox/+documentation HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /firefox/1.0/+documentation HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /ubuntu/+documentation HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /ubuntu/hoary/+documentation HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /mozilla/+documentation HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...
 
@@ -152,9 +152,9 @@ project, distro and distroseries.
 Some of the listings are supposed to indicate if there is an informational
 spec there using a badge:
 
-    >>> print http("""
+    >>> print(http("""
     ... GET /kubuntu/+documentation HTTP/1.1
-    ... """)
+    ... """))
     HTTP/1.1 200 Ok
     ...Activating Usplash...
 
@@ -276,7 +276,7 @@ blueprint targets:
     >>> browser.url
     'http://blueprints.launchpad.test/mozilla/+specs?show=all'
     >>> specs = find_tag_by_id(browser.contents, 'speclisting')
-    >>> print extract_text(specs)
+    >>> print(extract_text(specs))
     Priority Blueprint Design Delivery Assignee Project Series
     High     svg-support Approved Beta Available Carlos ... firefox
     Medium   canvas      New Unknown firefox
@@ -298,6 +298,6 @@ blueprint targets:
     >>> browser.getControl('Continue').click()
     >>> browser.open('http://blueprints.launchpad.test/mozilla/+series/1.0')
     >>> specs = find_tag_by_id(browser.contents, 'speclisting')
-    >>> print extract_text(specs)
+    >>> print(extract_text(specs))
     Priority Blueprint Design Delivery Assignee Project Series Milestone
     High svg-support Approved Beta Available Carlos ... firefox 1.0
diff --git a/lib/lp/blueprints/tests/test_doc.py b/lib/lp/blueprints/tests/test_doc.py
index dceb553..0a21d3d 100644
--- a/lib/lp/blueprints/tests/test_doc.py
+++ b/lib/lp/blueprints/tests/test_doc.py
@@ -8,6 +8,7 @@ from __future__ import absolute_import, print_function, unicode_literals
 import os
 
 from lp.services.testing import build_test_suite
+from lp.testing.pages import setUpGlobs
 from lp.testing.systemdocs import setUp
 
 
@@ -15,4 +16,6 @@ here = os.path.dirname(os.path.realpath(__file__))
 
 
 def test_suite():
-    return build_test_suite(here, setUp=lambda test: setUp(test, future=True))
+    return build_test_suite(
+        here, setUp=lambda test: setUp(test, future=True),
+        pageTestsSetUp=lambda test: setUpGlobs(test, future=True))