← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/soyuz-pagetests-future-imports into lp:launchpad

 

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

Commit message:
Convert pagetests under lp.soyuz to Launchpad's preferred __future__ imports.

Requested reviews:
  Colin Watson (cjwatson)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/soyuz-pagetests-future-imports/+merge/348796
-- 
Your team Launchpad code reviewers is subscribed to branch lp:launchpad.
=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
--- lib/lp/soyuz/browser/tests/archive-views.txt	2017-09-28 14:06:20 +0000
+++ lib/lp/soyuz/browser/tests/archive-views.txt	2018-07-01 09:01:37 +0000
@@ -38,29 +38,29 @@
 archive url if it is available (ie. an active archive that is not a copy)
 and None otherwise:
 
-    >>> print ppa_archive_view.archive_url
+    >>> print(ppa_archive_view.archive_url)
     http://ppa.launchpad.dev/cprov/ppa/ubuntu
-    >>> print copy_archive_view.archive_url
+    >>> print(copy_archive_view.archive_url)
     None
 
 The ArchiveView includes an archive_label property that returns either
 the string 'PPA' or 'archive' depending on whether the archive is a PPA
 (this is mainly for branding purposes):
 
-    >>> print ppa_archive_view.archive_label
+    >>> print(ppa_archive_view.archive_label)
     PPA
-    >>> print copy_archive_view.archive_label
+    >>> print(copy_archive_view.archive_label)
     archive
 
 The ArchiveView provides the html for the inline description editing widget.
 
-    >>> print ppa_archive_view.archive_description_html.title
+    >>> print(ppa_archive_view.archive_description_html.title)
     PPA description
 
 For convenience the ArchiveView also includes a build_counters property
 that returns a dict of the build count summary for the archive:
 
-    >>> print ppa_archive_view.build_counters
+    >>> print(ppa_archive_view.build_counters)
     {'failed': 1L, 'superseded': 0, 'total': 4L, ...
 
 An ArchiveView also includes an easy way to get any
@@ -82,7 +82,7 @@
 packages by status.
 
     >>> for term in ppa_archive_view.widgets['status_filter'].vocabulary:
-    ...     print term.title
+    ...     print(term.title)
     Published
     Superseded
 
@@ -90,7 +90,7 @@
 by series.
 
     >>> for term in ppa_archive_view.widgets['series_filter'].vocabulary:
-    ...     print term.title
+    ...     print(term.title)
     Breezy Badger Autotest
     Warty
 
@@ -107,7 +107,7 @@
 
     >>> def print_repository_usage(repository_usage):
     ...     for key, value in sorted(repository_usage.iteritems()):
-    ...         print '%s: %s' % (key, value)
+    ...         print('%s: %s' % (key, value))
 
 Celso PPA has some packages, but still below the quota.
 
@@ -195,7 +195,7 @@
 to get the current batch of publishing records for an archive:
 
     >>> for publishing in ppa_archive_view.batched_sources:
-    ...     print publishing.source_package_name
+    ...     print(publishing.source_package_name)
     cdrkit
     iceweasel
     pmount
@@ -209,7 +209,7 @@
     ...     method='GET',
     ...     query_string='field.series_filter=warty')
     >>> for publishing in filtered_view.batched_sources:
-    ...     print publishing.source_package_name
+    ...     print(publishing.source_package_name)
     iceweasel
     pmount
 
@@ -226,13 +226,13 @@
 
     >>> view = create_initialized_view(cprov.archive, name="+index")
 
-    >>> print view.dependencies
+    >>> print(view.dependencies)
     []
 
-    >>> print view.show_dependencies
+    >>> print(view.show_dependencies)
     False
 
-    >>> print view.has_disabled_dependencies
+    >>> print(view.has_disabled_dependencies)
     False
 
 'show_dependencies' is True for the PPA users, since the link for
@@ -243,13 +243,13 @@
 
     >>> view = create_initialized_view(cprov.archive, name="+index")
 
-    >>> print view.dependencies
+    >>> print(view.dependencies)
     []
 
-    >>> print view.show_dependencies
+    >>> print(view.show_dependencies)
     True
 
-    >>> print view.has_disabled_dependencies
+    >>> print(view.has_disabled_dependencies)
     False
 
 When there are any dependencies, 'show_dependencies' becomes True also
@@ -269,13 +269,13 @@
     >>> view = create_initialized_view(cprov.archive, name="+index")
 
     >>> for archive_dependency in view.dependencies:
-    ...     print archive_dependency.dependency.displayname
+    ...     print(archive_dependency.dependency.displayname)
     PPA for Zoing
 
-    >>> print view.show_dependencies
+    >>> print(view.show_dependencies)
     True
 
-    >>> print view.has_disabled_dependencies
+    >>> print(view.has_disabled_dependencies)
     False
 
 When a dependency is disabled, the 'has_disabled_dependencies' flag
@@ -289,26 +289,26 @@
     >>> view = create_initialized_view(cprov.archive, name="+index")
 
     >>> for archive_dependency in view.dependencies:
-    ...     print archive_dependency.dependency.displayname
+    ...     print(archive_dependency.dependency.displayname)
     PPA for Zoing
 
-    >>> print view.show_dependencies
+    >>> print(view.show_dependencies)
     True
 
-    >>> print view.has_disabled_dependencies
+    >>> print(view.has_disabled_dependencies)
     False
 
     >>> login('celso.providelo@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(cprov.archive, name="+index")
 
     >>> for archive_dependency in view.dependencies:
-    ...     print archive_dependency.dependency.displayname
+    ...     print(archive_dependency.dependency.displayname)
     PPA for Zoing
 
-    >>> print view.show_dependencies
+    >>> print(view.show_dependencies)
     True
 
-    >>> print view.has_disabled_dependencies
+    >>> print(view.has_disabled_dependencies)
     True
 
 Remove the testing PPA dependency to not influence subsequent tests.
@@ -323,11 +323,11 @@
     >>> def print_latest_updates(latest_updates):
     ...     for update in latest_updates:
     ...         arch_tags = [build.arch_tag for build in update['builds']]
-    ...         print "%s - %s %s" % (
+    ...         print("%s - %s %s" % (
     ...             update['title'],
     ...             update['status'],
     ...             " ".join(arch_tags),
-    ...             )
+    ...             ))
     >>> print_latest_updates(view.latest_updates)
     cdrkit - Failed to build: i386
     iceweasel - Successfully built
@@ -379,7 +379,7 @@
 The ArchiveView includes a helper to return the number of packages that
 are building as well as the number of packages waiting to build.
 
-    >>> print view.num_pkgs_building
+    >>> print(view.num_pkgs_building)
     {'building': 0, 'waiting': 0, 'total': 0}
 
 Let's set some builds appropriately to see the results.
@@ -395,7 +395,7 @@
     >>> builds = getUtility(IBinaryPackageBuildSet).getBuildsForArchive(
     ...     view.context)
     >>> for build in builds:
-    ...     print build.title
+    ...     print(build.title)
     hppa build of cdrkit 1.0 in ubuntu warty RELEASE
     hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE
     i386 build of pmount 0.1-1 in ubuntu warty RELEASE
@@ -428,7 +428,7 @@
 The archive index view overrides the default series filter to use the
 distroseries from the browser's user-agent, when applicable.
 
-    >>> print view.default_series_filter
+    >>> print(view.default_series_filter)
     None
 
     >>> view_warty = create_view(
@@ -438,24 +438,24 @@
     ...                     'Gecko/2009042523 Ubuntu/4.10 (whatever) '
     ...                     'Firefox/3.0.10')
     >>> view_warty.initialize()
-    >>> print view_warty.default_series_filter.name
+    >>> print(view_warty.default_series_filter.name)
     warty
 
 The archive index view also inherits the getSelectedFilterValue() method
 which can be used to find the currently selected value for both filters.
 
-    >>> print view_warty.getSelectedFilterValue('series_filter').name
+    >>> print(view_warty.getSelectedFilterValue('series_filter').name)
     warty
 
     >>> for status in view_warty.getSelectedFilterValue('status_filter'):
-    ...     print status.name
+    ...     print(status.name)
     PENDING
     PUBLISHED
 
 To enable the inline editing of the archive displayname, ArchiveView
 also provides a custom widget, displayname_edit_widget.
 
-    >>> print view.displayname_edit_widget.title
+    >>> print(view.displayname_edit_widget.title)
     Edit the displayname
 
 The view provides the is_probationary_ppa property. The archive's description
@@ -468,7 +468,7 @@
     >>> cprov.is_probationary
     True
 
-    >>> print view.archive_description_html.value
+    >>> print(view.archive_description_html.value)
     <p>http://example.dom/</p>
 
 The description is HTML escaped, and not linkified even when it contains HTML
@@ -478,7 +478,7 @@
     >>> cprov.archive.description = (
     ...     '<a href="http://example.com/";>http://example.com/</a>')
     >>> login(ANONYMOUS)
-    >>> print view.archive_description_html.value
+    >>> print(view.archive_description_html.value)
     <p>&lt;a href=&quot;http://example.com/&quot;&gt;http://example.com/&lt;/a&gt;</p>
 
 The PPA description is linked when the user has made a contribution.
@@ -496,7 +496,7 @@
     >>> contributor.is_probationary
     False
 
-    >>> print contributor_view.archive_description_html.value
+    >>> print(contributor_view.archive_description_html.value)
     <p><a rel="nofollow" href="http://example.dom/";>http://...example...
 
 
@@ -515,10 +515,10 @@
     >>> copy_archive_view = create_initialized_view(
     ...     copy_archive, name="+packages")
 
-    >>> print ppa_archive_view.page_title
+    >>> print(ppa_archive_view.page_title)
     Packages in ...PPA for Celso Providelo...
 
-    >>> print copy_archive_view.page_title
+    >>> print(copy_archive_view.page_title)
     Packages in ...Copy archive intrepid-security-rebuild...
 
 This view inherits from ArchiveViewBase and has all the
@@ -528,7 +528,7 @@
 Additionally, ArchivePackageView can display a string representation
 of the series supported by this archive.
 
-    >>> print ppa_archive_view.series_list_string
+    >>> print(ppa_archive_view.series_list_string)
     Breezy Badger Autotest and Warty
 
     >>> copy_archive_view.series_list_string
@@ -537,7 +537,7 @@
 The view also has a page_title property and can indicate whether the context
 is a copy archive.
 
-    >>> print copy_archive_view.page_title
+    >>> print(copy_archive_view.page_title)
     Packages in ...Copy archive intrepid-security-rebuild...
 
     >>> copy_archive_view.is_copy
@@ -583,7 +583,7 @@
 available sources are presented with empty filter.
 
     >>> for pub in view.batched_sources:
-    ...     print pub.displayname
+    ...     print(pub.displayname)
     cdrkit 1.0 in breezy-autotest
     iceweasel 1.0 in warty
     pmount 0.1-1 in warty
@@ -596,7 +596,7 @@
     ...     query_string="field.name_filter=pmount")
 
     >>> for pub in view.batched_sources:
-    ...     print pub.displayname
+    ...     print(pub.displayname)
     pmount 0.1-1 in warty
 
 The 'name_filter' is decoded as UTF-8 before futher processing. If it
@@ -617,7 +617,7 @@
     ...     query_string="field.series_filter=warty")
 
     >>> for pub in view.batched_sources:
-    ...     print pub.displayname
+    ...     print(pub.displayname)
     iceweasel 1.0 in warty
     pmount 0.1-1 in warty
 
@@ -629,7 +629,7 @@
     ...     form={'batch': '1', 'start': '1'})
 
     >>> for pub in view.batched_sources:
-    ...     print pub.displayname
+    ...     print(pub.displayname)
     pmount 0.1-1 in warty
 
 When submitted, deletions immediately take effect resulting in a page
@@ -690,10 +690,10 @@
 
 The view's h1 heading and leaf breadcrumb are equivalent.
 
-    >>> print view.label
+    >>> print(view.label)
     Edit PPA dependencies
 
-    >>> print view.page_title
+    >>> print(view.page_title)
     Edit PPA dependencies
 
 There is a property indicating whether or not the context PPA has
@@ -711,7 +711,7 @@
 'dependency_candidate' input field, where the user can directly type
 the owner of the PPA they want to mark as dependency.
 
-    >>> print view.focusedElementScript()
+    >>> print(view.focusedElementScript())
     <!--
     setFocusByName('field.dependency_candidate');
     // -->
@@ -760,20 +760,20 @@
 
     >>> [dependency] = view.widgets.get('selected_dependencies').vocabulary
 
-    >>> print dependency.value.displayname
+    >>> print(dependency.value.displayname)
     PPA for Mark Shuttleworth
 
-    >>> print dependency.token
+    >>> print(dependency.token)
     ~mark/ubuntu/ppa
 
-    >>> print dependency.title.escapedtext
+    >>> print(dependency.title.escapedtext)
     <a href="http://launchpad.dev/~mark/+archive/ubuntu/ppa";>PPA for Mark
     Shuttleworth</a>
 
 The form focus, now that we have a recorded dependencies, is set to the
 first listed dependency.
 
-    >>> print view.focusedElementScript()
+    >>> print(view.focusedElementScript())
     <!--
     setFocusByName('field.selected_dependencies');
     // -->
@@ -792,13 +792,13 @@
 
     >>> [dependency] = view.widgets.get('selected_dependencies').vocabulary
 
-    >>> print dependency.value.displayname
+    >>> print(dependency.value.displayname)
     PPA for Mark Shuttleworth
 
-    >>> print dependency.token
+    >>> print(dependency.token)
     ~mark/ubuntu/ppa
 
-    >>> print dependency.title
+    >>> print(dependency.title)
     PPA for Mark Shuttleworth
 
 If we remove the just-added dependency, the view gets back to its
@@ -829,7 +829,7 @@
     >>> view.has_dependencies
     False
 
-    >>> print view.focusedElementScript()
+    >>> print(view.focusedElementScript())
     <!--
     setFocusByName('field.dependency_candidate');
     // -->
@@ -841,7 +841,7 @@
     >>> primary_dependencies = view.widgets.get(
     ...     'primary_dependencies').vocabulary
     >>> for dependency in primary_dependencies:
-    ...     print dependency.value
+    ...     print(dependency.value)
     Release
     Security
     Updates
@@ -860,14 +860,14 @@
     ...     'primary_components').vocabulary
     >>> for term in primary_components:
     ...     if term.value is not None:
-    ...         print term.value.name
+    ...         print(term.value.name)
     ...     else:
-    ...         print term.value
+    ...         print(term.value)
     multiverse
     None
 
-    >>> print view.widgets.get(
-    ...     'primary_components')._getCurrentValue().name
+    >>> print(view.widgets.get(
+    ...     'primary_components')._getCurrentValue().name)
     multiverse
 
 The form validation code identifies attempts to change the primary
@@ -946,11 +946,11 @@
     >>> view = create_initialized_view(
     ...     cprov.archive, name="+edit-dependencies")
 
-    >>> print view.widgets.get(
-    ...     'primary_dependencies')._getCurrentValue().title
+    >>> print(view.widgets.get(
+    ...     'primary_dependencies')._getCurrentValue().title)
     Proposed
 
-    >>> print view.widgets.get('primary_components')._getCurrentValue()
+    >>> print(view.widgets.get('primary_components')._getCurrentValue())
     None
 
 Overriding the primary dependencies back to the 'default' value
@@ -1001,7 +1001,7 @@
     ...     form=add_private_form)
 
     >>> for error in view.errors:
-    ...     print error
+    ...     print(error)
     You don&#x27;t have permission to use this dependency.
 
 When we grant access to Celso for viewing the private PPA, by making
@@ -1019,7 +1019,7 @@
     ...     form=add_private_form)
 
     >>> for error in view.errors:
-    ...     print error
+    ...     print(error)
     Public PPAs cannot depend on private ones.
 
 Finally, we try with a private PPA of Celso's. That's enough for
@@ -1042,7 +1042,7 @@
 
     >>> dependencies = view.widgets.get('selected_dependencies').vocabulary
     >>> for dependency in dependencies:
-    ...     print dependency.value.displayname
+    ...     print(dependency.value.displayname)
     PPA for Pirulito Team
 
 Remove Celso's membership on the new team and disable his PPA so we don't
@@ -1099,7 +1099,7 @@
 `IArchiveSet.getPPAsForUser`).
 
     >>> for ppa in view.ppas_for_user:
-    ...     print ppa.displayname
+    ...     print(ppa.displayname)
     PPA for Celso Providelo
 
 The second shows whether or not the current user is allowed to perform
@@ -1122,7 +1122,7 @@
     ...     cprov.archive, name="+copy-packages")
 
     >>> for ppa in view.ppas_for_user:
-    ...     print ppa.displayname
+    ...     print(ppa.displayname)
     PPA for No Privileges Person
 
     >>> view.can_copy
@@ -1145,7 +1145,7 @@
     ...     cprov.archive, name="+copy-packages")
 
     >>> for ppa in view.ppas_for_user:
-    ...     print ppa.displayname
+    ...     print(ppa.displayname)
     PPA for Celso Providelo
     PPA for No Privileges Person
 
@@ -1167,7 +1167,7 @@
     ...     cprov.archive, name="+copy-packages")
 
     >>> for ppa in view.ppas_for_user:
-    ...     print ppa.displayname
+    ...     print(ppa.displayname)
     PPA for Celso Providelo
 
     # Re-enable No-Priv's PPA.
@@ -1179,7 +1179,7 @@
     >>> view = create_initialized_view(
     ...     cprov.archive, name="+copy-packages")
 
-    >>> print view.ppas_for_user
+    >>> print(view.ppas_for_user)
     []
 
     >>> view.can_copy
@@ -1205,7 +1205,7 @@
     ...     cprov.archive, name="+copy-packages")
 
     >>> for ppa in view.ppas_for_user:
-    ...     print ppa.displayname
+    ...     print(ppa.displayname)
     PPA for Celso Providelo
     PPA for Ubuntu Team
 
@@ -1223,7 +1223,7 @@
     ...     ubuntu.main_archive, name="+copy-packages")
 
     >>> for ppa in view.ppas_for_user:
-    ...     print ppa.displayname
+    ...     print(ppa.displayname)
     PPA for Celso Providelo
     PPA for Ubuntu Team
 
@@ -1264,14 +1264,14 @@
     >>> archive_widget.required
     False
 
-    >>> print archive_widget.translate(archive_widget._messageNoValue)
+    >>> print(archive_widget.translate(archive_widget._messageNoValue))
     This PPA
 
     >>> for item in archive_widget.vocabulary:
-    ...     print item.title
+    ...     print(item.title)
     PPA for Ubuntu Team [~ubuntu-team/ubuntu/ppa]
 
-    >>> print archive_widget.getInputValue() == cprov.archive
+    >>> print(archive_widget.getInputValue() == cprov.archive)
     True
 
 The 'destination_series' widget behaves similarly, it contains all
@@ -1283,17 +1283,17 @@
     >>> series_widget.required
     False
 
-    >>> print archive_widget.translate(series_widget._messageNoValue)
+    >>> print(archive_widget.translate(series_widget._messageNoValue))
     The same series
 
     >>> for item in series_widget.vocabulary:
-    ...     print item.title
+    ...     print(item.title)
     Breezy Badger Autotest
     Grumpy
     Hoary
     Warty
 
-    >>> print series_widget.getInputValue()
+    >>> print(series_widget.getInputValue())
     None
 
 The 'destination_archive' widget behaves differently depending on
@@ -1318,11 +1318,11 @@
     True
 
     >>> for item in archive_widget.vocabulary:
-    ...     print item.title
+    ...     print(item.title)
     PPA for Celso Providelo [~cprov/ubuntu/ppa]
     PPA for No Privileges Person [~no-priv/ubuntu/ppa]
 
-    >>> print archive_widget.getInputValue()
+    >>> print(archive_widget.getInputValue())
     Traceback (most recent call last):
     ...
     WidgetInputError: ('destination_archive',
@@ -1356,7 +1356,7 @@
 Now, as Celso we will try to copy the just created 'private' source to
 the public Ubuntu-team PPA, which is empty.
 
-    >>> print private_source.displayname
+    >>> print(private_source.displayname)
     foocomm 2.0-1 in hoary
 
     >>> ubuntu_team_ppa.getPublishedSources().count()
@@ -1381,7 +1381,7 @@
 
     >>> from lp.testing.pages import extract_text
     >>> for notification in view.request.response.notifications:
-    ...     print extract_text(notification.message)
+    ...     print(extract_text(notification.message))
     Requested sync of 1 package to PPA for Ubuntu Team.
     Please allow some time for this to be processed.
 
@@ -1397,14 +1397,14 @@
     ...     ubuntu_team_ppa)
     >>> with dbuser(config.IPlainPackageCopyJobSource.dbuser):
     ...     JobRunner([job]).runAll()
-    >>> print job.status.name
+    >>> print(job.status.name)
     COMPLETED
 
 The copy results in a pending source publication.
 
     >>> [copied_source] = ubuntu_team_ppa.getPublishedSources(
     ...     name="foocomm", exact_match=True)
-    >>> print copied_source.displayname
+    >>> print(copied_source.displayname)
     foocomm 2.0-1 in hoary
 
 If we run the same copy again, it will fail.
@@ -1422,7 +1422,7 @@
     ...     ubuntu_team_ppa)
     >>> with dbuser(config.IPlainPackageCopyJobSource.dbuser):
     ...     JobRunner([job]).runAll()
-    >>> print job.status.name
+    >>> print(job.status.name)
     FAILED
 
 The job failure is shown in the UI.
@@ -1435,8 +1435,8 @@
     >>> ubuntu_team_ppa_view.has_pending_copy_jobs is not None
     True
     >>> for job in ubuntu_team_ppa_view.package_copy_jobs:
-    ...     print job.status.title, job.package_name, job.package_version
-    ...     print job.error_message
+    ...     print(job.status.title, job.package_name, job.package_version)
+    ...     print(job.error_message)
     Failed foocomm 2.0-1
     foocomm 2.0-1 in hoary (same version already building in the destination
     archive for Hoary)
@@ -1458,44 +1458,45 @@
 of the form:
     deb scheme://domain/ suite component[s]
 
-    >>> print validate_external_dependencies(
+    >>> def print_validate_external_dependencies(ext_deps):
+    ...     for error in validate_external_dependencies(ext_deps):
+    ...         print(error)
+
+    >>> print_validate_external_dependencies(
     ...     "deb http://example.com/ karmic main")
-    []
 
 Multiple entries are valid, separated by newlines:
 
-    >>> print validate_external_dependencies(
+    >>> print_validate_external_dependencies(
     ...     "deb http://example.com/ karmic main\n"
     ...     "deb http://example.com/ karmic restricted")
-    []
 
 If the line does not start with the word "deb" it fails:
 
-    >>> print validate_external_dependencies(
+    >>> print_validate_external_dependencies(
     ...     "deb http://example.com/ karmic universe\n"
     ...     "dab http://example.com/ karmic main")
-    ["dab http://example.com/ karmic main: Must start with 'deb'"]
+    dab http://example.com/ karmic main: Must start with 'deb'
 
 If the line has too few parts it fails.  Here we're missing a suite:
 
-    >>> print validate_external_dependencies(
+    >>> print_validate_external_dependencies(
     ...     "deb http://example.com/ karmic universe\n"
     ...     "deb http://example.com/ main")
-    ["'deb http://example.com/ main'
-        is not a complete and valid sources.list entry"]
+    'deb http://example.com/ main'
+        is not a complete and valid sources.list entry
 
 If the URL looks invalid, it fails:
 
-    >>> print validate_external_dependencies(
+    >>> print_validate_external_dependencies(
     ...     "deb http://example.com/ karmic universe\n"
     ...     "deb example.com/ karmic main")
-    ['deb example.com/ karmic main: Invalid URL']
+    deb example.com/ karmic main: Invalid URL
 
 Options are permitted:
 
-    >>> print validate_external_dependencies(
+    >>> print_validate_external_dependencies(
     ...     "deb [trusted=yes] http://example.com/ karmic main")
-    []
-    >>> print validate_external_dependencies(
+    >>> print_validate_external_dependencies(
     ...     "deb [trusted=yes] example.com/ karmic main")
-    ['deb [trusted=yes] example.com/ karmic main: Invalid URL']
+    deb [trusted=yes] example.com/ karmic main: Invalid URL

=== modified file 'lib/lp/soyuz/browser/tests/archivesubscription-views.txt'
--- lib/lp/soyuz/browser/tests/archivesubscription-views.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/browser/tests/archivesubscription-views.txt	2018-07-01 09:01:37 +0000
@@ -34,7 +34,7 @@
     >>> login('celso.providelo@xxxxxxxxxxxxx')
     >>> view = create_initialized_view(
     ...     cprov_private_ppa, name="+subscriptions")
-    >>> print view.label
+    >>> print(view.label)
     Manage access to PPA named pppa for Celso Providelo
 
 Initially the view does not display any subscribers, as can be seen
@@ -54,7 +54,7 @@
     ...         'field.date_expires': '',
     ...         'field.subscriber': ''})
     >>> for error in view.errors:
-    ...     print error.field_name, view.getFieldError(error.field_name)
+    ...     print(error.field_name, view.getFieldError(error.field_name))
     subscriber Required input is missing.
 
 The view can be used to add a new subscriber:
@@ -75,7 +75,7 @@
 
     >>> view.request.response.getStatus()
     302
-    >>> print view.request.response.getHeader('location')
+    >>> print(view.request.response.getHeader('location'))
     https://launchpad.dev/~cprov/+archive/ppa/+subscriptions
     >>> view.has_subscriptions
     True
@@ -83,7 +83,7 @@
 After adding a subscriber, the view includes a relevant notification:
 
     >>> for notification in view.request.notifications:
-    ...     print notification.message
+    ...     print(notification.message)
     You have granted access for Andrew Bennetts to install software
     from PPA named pppa for Celso Providelo.
     Andrew Bennetts will be notified of the access via email.
@@ -92,7 +92,7 @@
 subscribers:
 
     >>> for subscription in view.subscriptions:
-    ...     print subscription.subscriber.displayname
+    ...     print(subscription.subscriber.displayname)
     Andrew Bennetts
 
     >>> transaction.commit()
@@ -111,7 +111,7 @@
 In this case the view will include validation errors:
 
     >>> for error in view.errors:
-    ...     print error
+    ...     print(error)
     Andrew Bennetts is already subscribed.
 
 But the same person can be a subscriber of other archives:
@@ -127,7 +127,7 @@
     ...         })
 
     >>> for subscription in view.subscriptions:
-    ...     print subscription.subscriber.displayname
+    ...     print(subscription.subscriber.displayname)
     Andrew Bennetts
 
 A second subscriber can be added, this time a we'll add a team as a
@@ -145,7 +145,7 @@
     ...         'field.actions.add': 'Add'
     ...         })
     >>> for error in view.errors:
-    ...     print error
+    ...     print(error)
     The expiry date must be in the future.
 
 So we try again with an expiry date in the future:
@@ -165,7 +165,7 @@
     >>> view.errors
     []
     >>> for subscription in view.subscriptions:
-    ...     print subscription.subscriber.displayname
+    ...     print(subscription.subscriber.displayname)
     Launchpad Developers
     Andrew Bennetts
 
@@ -180,7 +180,7 @@
     >>> spiv_subscription = getUtility(IArchiveSubscriberSet).getByArchive(
     ...     cprov_private_ppa).one()
     >>> view = create_initialized_view(spiv_subscription, name="+edit")
-    >>> print view.label
+    >>> print(view.label)
     Edit Andrew Bennetts's access to PPA named pppa for Celso Providelo
 
 The ArchiveSubscriptionEditView presents the expiry and description ready
@@ -189,13 +189,13 @@
     >>> view.field_names
     ['date_expires', 'description']
     >>> for action in view.actions:
-    ...     print action.label
+    ...     print(action.label)
     Save
     Revoke access
 
 The ArchiveSubscriptionEditView has a next_url helper property.
 
-    >>> print view.next_url
+    >>> print(view.next_url)
     http://launchpad.dev/~cprov/+archive/ubuntu/pppa/+subscriptions
 
 The ArchiveSubscriptionEditView can be used to update the description field:
@@ -207,7 +207,7 @@
     ...         'field.date_expires': '',
     ...         'field.actions.update': 'Update'
     ...     })
-    >>> print spiv_subscription.description
+    >>> print(spiv_subscription.description)
     Updated description
 
 Like the create view, the update view will not accept a date in the past:
@@ -220,7 +220,7 @@
     ...         'field.actions.update': 'Update'
     ...     })
     >>> for error in view.errors:
-    ...     print error
+    ...     print(error)
     The expiry date must be in the future.
 
 But a date in the future is fine:
@@ -238,20 +238,20 @@
 The ArchiveSubscriptionEditView can be used to cancel a subscription:
 
     >>> current_status = spiv_subscription.status
-    >>> print current_status.name
+    >>> print(current_status.name)
     CURRENT
     >>> view = create_initialized_view(
     ...     spiv_subscription, name="+edit", method="POST",
     ...     form={'field.actions.cancel': 'Cancel subscription'})
-    >>> print spiv_subscription.status.name
+    >>> print(spiv_subscription.status.name)
     CANCELLED
-    >>> print spiv_subscription.cancelled_by.name
+    >>> print(spiv_subscription.cancelled_by.name)
     cprov
 
 After canceling a subscription, a relevant notification is added to the view.
 
     >>> for notification in view.request.notifications:
-    ...     print notification.message
+    ...     print(notification.message)
     You have revoked Andrew Bennetts&#x27;s access to PPA named pppa for
     Celso Providelo.
 
@@ -287,11 +287,11 @@
     ...     for subscription_and_token in view.subscriptions_with_tokens:
     ...         subscription = subscription_and_token['subscription']
     ...         token = subscription_and_token['token']
-    ...         print subscription.archive.displayname
+    ...         print(subscription.archive.displayname)
     ...         token_text = "None"
     ...         if token:
     ...             token_text = "Token"
-    ...         print token_text
+    ...         print(token_text)
 
     >>> print_subscriptions_with_tokens()
     PPA named pppa for Mark Shuttleworth       None
@@ -324,12 +324,12 @@
     >>> spiv_subscription = PersonalArchiveSubscription(
     ...     spiv_subscription.subscriber, spiv_subscription.archive)
     >>> view = create_initialized_view(spiv_subscription, name="+index")
-    >>> print view.label
+    >>> print(view.label)
     Access to PPA named pppa for Celso Providelo
 
 Initially the subscription does not have an active token:
 
-    >>> print view.active_token
+    >>> print(view.active_token)
     None
 
 But if 'activate' is posted, the view will generate a new token and
@@ -343,10 +343,10 @@
 Now the view can then access the token and the source list entries sub-view:
 
     >>> view = create_initialized_view(spiv_subscription, name="+index")
-    >>> print view.active_token.person.displayname
+    >>> print(view.active_token.person.displayname)
     Andrew Bennetts
 
-    >>> print view.sources_list_entries.context.archive_url
+    >>> print(view.sources_list_entries.context.archive_url)
     http://spiv:...@private-ppa.launchpad.dev/cprov/pppa/...
 
 The view can also be used to regenerate the source.list entries.
@@ -357,7 +357,7 @@
     >>> view.request.response.getStatus()
     302
     >>> for notification in view.request.notifications:
-    ...     print notification.message
+    ...     print(notification.message)
     Launchpad has generated the new password you...
 
     >>> view = create_initialized_view(spiv_subscription, name="+index")

=== modified file 'lib/lp/soyuz/browser/tests/distribution-views.txt'
--- lib/lp/soyuz/browser/tests/distribution-views.txt	2012-07-09 22:56:14 +0000
+++ lib/lp/soyuz/browser/tests/distribution-views.txt	2018-07-01 09:01:37 +0000
@@ -62,13 +62,13 @@
     >>> distro_pkg_search_view.search_by_binary_name
     True
     >>> for package in distro_pkg_search_view.search_results:
-    ...     print package.name
+    ...     print(package.name)
     mozilla-firefox
 
 Additionally, a helper property 'source_search_url' is included providing
 easy access to the equivalent search on sources:
 
-    >>> print distro_pkg_search_view.source_search_url
+    >>> print(distro_pkg_search_view.source_search_url)
     http://launchpad.dev/ubuntu/+search?search_type=source&text=a
 
 Unicode form variables remain encoded as UTF-8 (as expected by the
@@ -79,7 +79,7 @@
     ...     form={'text': u'\xe7'},
     ...     query_string='text=%C3%A7')
 
-    >>> print distro_pkg_search_view.source_search_url
+    >>> print(distro_pkg_search_view.source_search_url)
     http://launchpad.dev/ubuntu/+search?search_type=source&text=%C3%A7
 
 But users can specify that the search should be on source-package-names
@@ -93,7 +93,7 @@
     >>> distro_pkg_search_view.search_by_binary_name
     False
     >>> for package in distro_pkg_search_view.search_results:
-    ...     print package.name
+    ...     print(package.name)
     alsa-utils
     commercialpackage
     foobar
@@ -120,8 +120,8 @@
     ...     ubuntu, name="+search",
     ...     form={'text': ' a '},
     ...     query_string='text=a')
-    >>> distro_pkg_search_view.text
-    'a'
+    >>> print(distro_pkg_search_view.text)
+    a
 
 If there is more than one text parameter value, the last one is used.
 
@@ -129,8 +129,8 @@
     ...     ubuntu, name="+search",
     ...     form={'text': ['a','b']},
     ...     query_string='text=a&text=b')
-    >>> distro_pkg_search_view.text
-    'b'
+    >>> print(distro_pkg_search_view.text)
+    b
 
 Exact matches
 .............
@@ -147,7 +147,7 @@
     >>> distro_pkg_search_view.has_exact_matches
     True
     >>> for package in distro_pkg_search_view.exact_matches:
-    ...     print package.name
+    ...     print(package.name)
     mozilla-firefox
 
 The view can also help the template know when to display exact matches.
@@ -214,10 +214,10 @@
 names are returned. An ellipse is used to indicate when more than five
 names match:
 
-    >>> print distro_pkg_search_view._listFirstFiveMatchingNames(
+    >>> print(distro_pkg_search_view._listFirstFiveMatchingNames(
     ...     'moz',
     ...     'mozilla-firefox mozilla-data moziki '
-    ...     'limozine moza lamoz') # doctest: -ELLIPSIS
+    ...     'limozine moza lamoz')) # doctest: -ELLIPSIS
     mozilla-firefox, mozilla-data, moziki, limozine, moza, ...
 
 
@@ -231,5 +231,5 @@
     1
 
     >>> for pkg in search_results:
-    ...     print pkg.name
+    ...     print(pkg.name)
     mozilla-firefox

=== modified file 'lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt'
--- lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt	2018-05-04 21:59:32 +0000
+++ lib/lp/soyuz/browser/tests/distributionsourcepackagerelease-views.txt	2018-07-01 09:01:37 +0000
@@ -17,7 +17,7 @@
 
     >>> dspr_view = create_initialized_view(dspr, name="+index")
 
-    >>> print dspr.title
+    >>> print(dspr.title)
     testing-dspr 1.0 source package in ubuntutest
 
     >>> dspr_view.page_title
@@ -29,7 +29,7 @@
 tofiles allowing them to be downloaded using `dget`.
 
     >>> for source_file in dspr_view.files:
-    ...     print source_file.filename, source_file.http_url
+    ...     print(source_file.filename, source_file.http_url)
     testing-dspr_1.0.dsc
     http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc
 
@@ -37,10 +37,10 @@
 not. When the upload was signed by someone else than the source
 creator, the upload signer is the sponsor.
 
-    >>> print dspr.creator.displayname
+    >>> print(dspr.creator.displayname)
     Foo Bar
 
-    >>> print dspr_view.sponsor
+    >>> print(dspr_view.sponsor)
     None
 
     # Forcibly change the SPR.creator, so the source becomes 'sponsored'.
@@ -52,10 +52,10 @@
 
     >>> dspr_view = create_initialized_view(dspr, name="+index")
 
-    >>> print dspr.creator.displayname
+    >>> print(dspr.creator.displayname)
     Novice
 
-    >>> print dspr_view.sponsor.displayname
+    >>> print(dspr_view.sponsor.displayname)
     Foo Bar
 
 'currently_published' contains only PUBLISHED publications for this
@@ -77,7 +77,7 @@
 
     >>> dspr_view = create_initialized_view(dspr, name="+index")
     >>> for publishing in dspr_view.currently_published:
-    ...     print publishing.distroseries.name
+    ...     print(publishing.distroseries.name)
     breezy-autotest
 
     # Copy the testing publication to another series.
@@ -93,7 +93,7 @@
 
     >>> dspr_view = create_initialized_view(dspr, name="+index")
     >>> for publishing in dspr_view.currently_published:
-    ...     print publishing.distroseries.name
+    ...     print(publishing.distroseries.name)
     hoary-test
     breezy-autotest
 
@@ -104,8 +104,8 @@
     ...     for build_group in dspr_view.grouped_builds:
     ...         arch_tags = ' '.join(
     ...             build.arch_tag for build in build_group['builds'])
-    ...         print '%s: %s' % (build_group['distroseries'].name, arch_tags)
-    ...     print 'END'
+    ...         print('%s: %s' % (build_group['distroseries'].name, arch_tags))
+    ...     print('END')
 
     >>> print_grouped_builds()
     END

=== modified file 'lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt'
--- lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt	2015-09-04 12:19:07 +0000
+++ lib/lp/soyuz/browser/tests/distroseriesqueue-views.txt	2018-07-01 09:01:37 +0000
@@ -154,7 +154,7 @@
   ...     (breezy_autotest, request), name="+queue")
   >>> queue_view.setupQueueList()
   >>> queue_view.performQueueAction()
-  >>> print queue_view.error
+  >>> print(queue_view.error)
   You do not have permission to act on queue items.
 
   >>> getUtility(IPackageUploadSet).get(1).status.name
@@ -197,7 +197,7 @@
   ...     (breezy_autotest, request), name="+queue")
   >>> queue_view.setupQueueList()
   >>> queue_view.performQueueAction()
-  >>> print queue_view.error
+  >>> print(queue_view.error)
   You do not have permission to act on queue items.
 
   >>> target.status.name
@@ -290,7 +290,7 @@
 
     >>> binary_packages = foo_upload.queue_root.builds[0].build.binarypackages
     >>> for binarypackage in binary_packages:
-    ...     print binarypackage.name, queue_view.is_new(binarypackage)
+    ...     print(binarypackage.name, queue_view.is_new(binarypackage))
     foo False
     foo-dev True
 

=== modified file 'lib/lp/soyuz/browser/tests/publishing-views.txt'
--- lib/lp/soyuz/browser/tests/publishing-views.txt	2018-05-04 21:59:32 +0000
+++ lib/lp/soyuz/browser/tests/publishing-views.txt	2018-07-01 09:01:37 +0000
@@ -33,7 +33,7 @@
     >>> view = create_initialized_view(foo_pub, "+listing-compact")
     >>> view.wasDeleted()
     True
-    >>> print view.context.removal_comment
+    >>> print(view.context.removal_comment)
     None
     >>> view.removal_comment
     u'None provided.'
@@ -43,9 +43,9 @@
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> foo_pub.removal_comment = "It had to go."
 
-    >>> print view.context.removal_comment
+    >>> print(view.context.removal_comment)
     It had to go.
-    >>> print view.removal_comment
+    >>> print(view.removal_comment)
     It had to go.
 
 
@@ -105,10 +105,10 @@
 
 We probe the 'is_source' and 'is_binary' properties.
 
-    >>> print source_details_view.is_source
+    >>> print(source_details_view.is_source)
     True
 
-    >>> print source_details_view.is_binary
+    >>> print(source_details_view.is_binary)
     False
 
 Similarly, we use one of the 'iceweasel' binaries published in Celso's
@@ -120,10 +120,10 @@
     >>> binary_details_view = create_initialized_view(
     ...     iceweasel_binary_pub, "+record-details")
 
-    >>> print binary_details_view.is_source
+    >>> print(binary_details_view.is_source)
     False
 
-    >>> print binary_details_view.is_binary
+    >>> print(binary_details_view.is_binary)
     True
 
 Make sure the 'timestamp_map' class attribute in BasePublishingRecordView
@@ -135,8 +135,8 @@
     >>> from lp.soyuz.browser.publishing import (
     ...     BasePublishingRecordView)
     >>> for pps in PackagePublishingStatus.items:
-    ...     print '%s -> %s' % (
-    ...         pps, BasePublishingRecordView.timestamp_map[pps])
+    ...     print('%s -> %s' % (
+    ...         pps, BasePublishingRecordView.timestamp_map[pps]))
     Pending -> datecreated
     Published -> datepublished
     Superseded -> datesuperseded
@@ -146,21 +146,21 @@
 Any key that's not in the PackagePublishingStatus enumeration will cause an
 exception to be thrown.
 
-    >>> print BasePublishingRecordView.timestamp_map['key_not_there']
+    >>> print(BasePublishingRecordView.timestamp_map['key_not_there'])
     Traceback (most recent call last):
     ...
-    KeyError: 'key_not_there'
+    KeyError: u'key_not_there'
 
 The view knows how to render a publication's phased update percentage.
 
-    >>> print binary_details_view.phased_update_percentage
+    >>> print(binary_details_view.phased_update_percentage)
 
     >>> login('celso.providelo@xxxxxxxxxxxxx')
     >>> iceweasel_binary_pub_phased = iceweasel_binary_pub.changeOverride(
     ...     new_phased_update_percentage=50)
     >>> binary_details_view = create_initialized_view(
     ...     iceweasel_binary_pub_phased, "+record-details")
-    >>> print binary_details_view.phased_update_percentage
+    >>> print(binary_details_view.phased_update_percentage)
     50% of users
 
 BinaryPackagePublishingHistory:+listing-summary is included in
@@ -169,11 +169,11 @@
 
     >>> binary_summary_view = create_initialized_view(
     ...     iceweasel_binary_pub, "+listing-summary")
-    >>> print binary_summary_view.phased_update_percentage
+    >>> print(binary_summary_view.phased_update_percentage)
 
     >>> binary_summary_view_phased = create_initialized_view(
     ...     iceweasel_binary_pub_phased, "+listing-summary")
-    >>> print binary_summary_view_phased.phased_update_percentage
+    >>> print(binary_summary_view_phased.phased_update_percentage)
     50% of users
 
 
@@ -190,12 +190,12 @@
 Create a small function for displaying the results:
 
     >>> def print_build_summary(summary):
-    ...     print "%s\n%s\nRelevant builds:\n%s" % (
+    ...     print("%s\n%s\nRelevant builds:\n%s" % (
     ...         summary['status'].title,
     ...         summary['status'].description,
     ...         "\n".join(
     ...             " - %s" % build.title for build in summary['builds'])
-    ...     )
+    ...     ))
 
     >>> print_build_summary(src_pub_record_view.build_status_summary)
     FULLYBUILT_PENDING
@@ -206,7 +206,7 @@
 The view also helps templates to decide on the icon that should be used
 to summarize the current state of the context's associated builds:
 
-    >>> print src_pub_record_view.build_status_img_src
+    >>> print(src_pub_record_view.build_status_img_src)
     /@@/build-success-publishing
 
 As well as some helpers to determine the publishing status from templates:
@@ -218,5 +218,5 @@
     True
 
     >>> for build in src_pub_record_view.pending_builds:
-    ...     print build.title
+    ...     print(build.title)
     i386 build of iceweasel 1.0 in ubuntu warty RELEASE

=== modified file 'lib/lp/soyuz/browser/tests/sourcepackage-views.txt'
--- lib/lp/soyuz/browser/tests/sourcepackage-views.txt	2015-11-24 01:44:28 +0000
+++ lib/lp/soyuz/browser/tests/sourcepackage-views.txt	2018-07-01 09:01:37 +0000
@@ -39,7 +39,7 @@
   >>> for pub in pmount_view.published_by_pocket():
   ...     pkg_versions = [
   ...         (p['spr'].version, p['component_name']) for p in pub['packages']]
-  ...     print pub['pocketdetails'].title, sorted(pkg_versions)
+  ...     print(pub['pocketdetails'].title, sorted(pkg_versions))
   Release [(u'0.1-2', u'main')]
   Security []
   Updates []
@@ -62,7 +62,7 @@
 have only on binary.
 
   >>> for bin_name, archs in firefox_view.binaries().items():
-  ...    print bin_name, archs
+  ...    print(bin_name, archs)
   mozilla-firefox [u'hppa', u'i386']
   mozilla-firefox-data [u'hppa', u'i386']
 
@@ -103,7 +103,7 @@
 
   >>> libc = ubuntu.getSourcePackage('libstdc++').getVersion('b8p')
   >>> libc_view = queryMultiAdapter((libc, request), name="+changelog")
-  >>> print libc_view.changelog_entry
+  >>> print(libc_view.changelog_entry)
   libstdc++ (9.9-1) hoary; urgency=high
   <BLANKLINE>
    * Placeholder

=== modified file 'lib/lp/soyuz/browser/tests/test_views.py'
--- lib/lp/soyuz/browser/tests/test_views.py	2018-02-01 18:44:21 +0000
+++ lib/lp/soyuz/browser/tests/test_views.py	2018-07-01 09:01:37 +0000
@@ -33,7 +33,8 @@
     for filename in filenames:
         path = filename
         one_test = LayeredDocFileSuite(
-            path, setUp=setUp, tearDown=tearDown,
+            path,
+            setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
             layer=LaunchpadFunctionalLayer,
             stdout_logging_level=logging.WARNING
             )

=== modified file 'lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt'
--- lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt	2016-10-01 10:50:49 +0000
+++ lib/lp/soyuz/stories/distribution/xx-distribution-packages.txt	2018-07-01 09:01:37 +0000
@@ -19,7 +19,7 @@
     'http://localhost/ubuntu/+search?text=pmount'
 
     >>> for tag in find_tags_by_class(browser.contents, 'package-matches'):
-    ...     print extract_text(tag).encode('us-ascii', 'replace')
+    ...     print(extract_text(tag).encode('us-ascii', 'replace'))
     pmount
     (Matching binaries: pmount.)
 
@@ -31,7 +31,7 @@
 
 Get pmount 0.1-2 version
 
-    >>> print browser.getLink("0.1-2").url
+    >>> print(browser.getLink("0.1-2").url)
     http://localhost/ubuntu/+source/pmount/0.1-2
 
 Ensure that the correct binaries appear on the search results.  We only
@@ -46,7 +46,7 @@
     >>> browser.open(
     ...     'http://localhost'
     ...     '/ubuntu/breezy-autotest/+package/mozilla-firefox')
-    >>> print browser.title
+    >>> print(browser.title)
     mozilla-firefox : Breezy Badger Autotest (6.6.6) : Ubuntu
 
 Now run a search for mozilla-firefox and check that it is found:
@@ -56,7 +56,7 @@
     >>> field.value = 'mozilla-firefox'
     >>> browser.getControl('Search', index=0).click()
     >>> for tag in find_tags_by_class(browser.contents, 'package-matches'):
-    ...     print extract_text(tag).encode('us-ascii', 'replace')
+    ...     print(extract_text(tag).encode('us-ascii', 'replace'))
     mozilla-firefox
     The Mozilla Firefox web browser
     (Matching binaries: mozilla-firefox, mozilla-firefox-data.)
@@ -149,7 +149,7 @@
 The page has an appropriate title and main heading.
 
     >>> from lp.services.helpers import backslashreplace
-    >>> print backslashreplace(user_browser.title)
+    >>> print(backslashreplace(user_browser.title))
     iceweasel package : Ubuntu
 
     >>> print_location(user_browser.contents)
@@ -166,16 +166,16 @@
 Under the title there's a short paragraph that says how many 'new' bugs
 and open questions the package has.
 
-    >>> print extract_text(find_tag_by_id(
-    ...     user_browser.contents, 'bugs-and-questions-summary'))
+    >>> print(extract_text(find_tag_by_id(
+    ...     user_browser.contents, 'bugs-and-questions-summary')))
     This package has 0 new bugs and 0 open questions.
 
 Links exist to jump to the query page for the new bugs and open questions.
 
-    >>> print user_browser.getLink("0 new bugs").url
+    >>> print(user_browser.getLink("0 new bugs").url)
     http://bugs.launchpad.dev/ubuntu/+source/iceweasel/+bugs?field.status:list=NEW
 
-    >>> print user_browser.getLink("0 open questions").url
+    >>> print(user_browser.getLink("0 open questions").url)
     http://answers.launchpad.dev/ubuntu/+source/iceweasel/+questions?field.status=OPEN
 
 The page also has a table that shows the distro series in which the package is
@@ -185,8 +185,8 @@
 with which pocket has each version, the component in which it's published, and
 the time elapsed since it was published.
 
-    >>> print extract_text(find_tag_by_id(
-    ...     user_browser.contents, 'packages_list'))
+    >>> print(extract_text(find_tag_by_id(
+    ...     user_browser.contents, 'packages_list')))
     The Warty Warthog Release (current stable release)      Set upstream link
       1.0  release  (main)  2006-04-11
 
@@ -196,13 +196,13 @@
 
     >>> expander_url = find_tags_by_class(
     ...     user_browser.contents, 'expander')[0]
-    >>> print expander_url
+    >>> print(expander_url)
     <a class="expander"
     href="/ubuntu/+archive/primary/+sourcepub/26/+listing-archive-extra"
     id="pub26-expander"></a>
 
     >>> browser.open(user_browser.getLink(id="pub26-expander").url)
-    >>> print extract_text(browser.contents)
+    >>> print(extract_text(browser.contents))
     Publishing details
     Published
       on 2006-04-11
@@ -221,12 +221,12 @@
 search includes a leading space in order to exclude the "Latest upload:"
 link.)
 
-    >>> print user_browser.getLink(" 1.0").attrs['href']
+    >>> print(user_browser.getLink(" 1.0").attrs['href'])
     /ubuntu/+source/iceweasel/1.0
 
 There's also a section on the page that gives some package information:
 
-    >>> print extract_text(find_tag_by_id(user_browser.contents, 'current'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents, 'current')))
     Package information
     Maintainer: Foo Bar
     Urgency:* Low Urgency
@@ -238,7 +238,7 @@
 And if the source has direct packaging linkage, the upstream's description
 is used in another section:
 
-    >>> print extract_text(find_tag_by_id(user_browser.contents, 'upstream'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents, 'upstream')))
     Upstream connections
     Launchpad doesn...t know which project and series this
     package belongs to...
@@ -247,7 +247,7 @@
 "Set upstream link" link.
 
     >>> user_browser.getLink("Set upstream link").click()
-    >>> print user_browser.url
+    >>> print(user_browser.url)
     http://launchpad.dev/ubuntu/warty/+source/iceweasel/+edit-packaging
 
 In step one the project is specified.
@@ -259,7 +259,7 @@
 In step two, one of the series for that project can be selected.
 
     >>> series_control = user_browser.getControl(name='field.productseries')
-    >>> print series_control.options
+    >>> print(series_control.options)
     ['trunk', '1.0']
     >>> series_control.value = ['trunk']
     >>> user_browser.getControl('Change').click()
@@ -268,7 +268,7 @@
 linked.
 
     >>> user_browser.open("http://launchpad.dev/ubuntu/+source/iceweasel/";)
-    >>> print extract_text(find_tag_by_id(user_browser.contents, 'upstream'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents, 'upstream')))
     Upstream connections
     The Mozilla Project...
     Mozilla Firefox...
@@ -285,22 +285,22 @@
 The page has a side-bar with a global actions menu, a "Get Involved"
 menu, and a "Subscribers" portlet.
 
-    >>> print extract_text(
-    ...     find_tag_by_id(user_browser.contents, 'global-actions'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(user_browser.contents, 'global-actions')))
     View full publishing history
     View full change log
     Subscribe to bug mail
     Edit bug mail
 
-    >>> print extract_text(
-    ...     find_tag_by_id(user_browser.contents, 'involvement'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(user_browser.contents, 'involvement')))
     Get Involved
     Report a bug
     Ask a question
 
-    >>> print extract_text(
+    >>> print(extract_text(
     ...     find_tag_by_id(user_browser.contents,
-    ...                    'portlet-structural-subscribers'))
+    ...                    'portlet-structural-subscribers')))
     Subscribers
     ...
 
@@ -315,7 +315,7 @@
 distroseries.  The distroseries are presented in order, most recent first.
 
     >>> browser.open("http://launchpad.dev/ubuntutest/+source/netapplet/";)
-    >>> print extract_text(find_tag_by_id(browser.contents, 'packages_list'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'packages_list')))
     Mock Hoary (active development)                   Set upstream link
       1.0.1a  release  (main)  ...
     Breezy Badger Autotest  (active development)      Set upstream link
@@ -327,7 +327,7 @@
 At the bottom of the page, the three latest PPA uploads of this source package
 are displayed.
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'ppa_packaging'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'ppa_packaging')))
     PPA named nightly for Odd owned by Odd
       Versions: Breezy Badger Autotest (0.8.2n3)
     PPA named beta for Even owned by Even
@@ -352,7 +352,7 @@
     >>> browser.open("http://launchpad.dev/ubuntu/+source/foobar/";)
     >>> browser.getLink("View full change log").click()
 
-    >>> print backslashreplace(browser.title)
+    >>> print(backslashreplace(browser.title))
     Change log : foobar package : Ubuntu
 
     >>> print_location(browser.contents)
@@ -374,7 +374,7 @@
 
     >>> first_header = find_tag_by_id(browser.contents,
     ...     "detail_foobar_1.0")
-    >>> print extract_text(first_header)
+    >>> print(extract_text(first_header))
     1.0
     Deleted in warty-release on 2006-12-02 (Reason: I do not like it.)
 
@@ -384,7 +384,7 @@
     ...     "http://launchpad.dev/ubuntu/+source/alsa-utils/+changelog";)
     >>> first_header = find_tag_by_id(browser.contents,
     ...     'detail_alsa-utils_1.0.9a-4ubuntu1')
-    >>> print extract_text(first_header)
+    >>> print(extract_text(first_header))
     1.0.9a-4ubuntu1
     Pending in warty-release since 2006-02-15 12:19:00 UTC
     Published in hoary-release on 2005-09-15
@@ -393,7 +393,7 @@
 release.
 
     >>> first_header_link = first_header.find('a')
-    >>> print extract_text(first_header_link)
+    >>> print(extract_text(first_header_link))
     1.0.9a-4ubuntu1
 
     >>> first_header_link.get('href')
@@ -405,7 +405,7 @@
 
     >>> first_body = find_tag_by_id(browser.contents,
     ...     'body_alsa-utils_1.0.9a-4ubuntu1')
-    >>> print extract_text(first_body)
+    >>> print(extract_text(first_body))
     alsa-utils (1.0.9a-4ubuntu1) hoary; urgency=low
     * Placeholder
     LP: #10
@@ -420,8 +420,8 @@
 
     >>> user_browser.open(
     ...     "http://launchpad.dev/ubuntu/+source/alsa-utils/+changelog";)
-    >>> print extract_text(find_tag_by_id(user_browser.contents,
-    ...     'body_alsa-utils_1.0.9a-4ubuntu1'))
+    >>> print(extract_text(find_tag_by_id(user_browser.contents,
+    ...     'body_alsa-utils_1.0.9a-4ubuntu1')))
     alsa-utils (1.0.9a-4ubuntu1) hoary; urgency=low
     ...
     -- Sample Person &lt;test@xxxxxxxxxxxxx&gt; Tue, 7 Feb 2006 12:10:08 +0300
@@ -442,7 +442,7 @@
     ...     "+changelog")
     >>> changelog = find_tag_by_id(
     ...     user_browser.contents, 'commercialpackage_1.0-1')
-    >>> print extract_text(changelog.find('a'))
+    >>> print(extract_text(changelog.find('a')))
     foo.bar@xxxxxxxxxxxxx
 
 
@@ -453,14 +453,14 @@
 displayed:
 
     >>> user_browser.open("http://launchpad.dev/ubuntu/+source/a52dec/";)
-    >>> print extract_text(find_tag_by_id(
-    ...     user_browser.contents, 'packages_list'))
+    >>> print(extract_text(find_tag_by_id(
+    ...     user_browser.contents, 'packages_list')))
 
 The package information portlet also reflects that the package is not present
 at all in the distribution.
 
-    >>> print extract_text(find_tag_by_id(
-    ...     user_browser.contents, 'current'))
+    >>> print(extract_text(find_tag_by_id(
+    ...     user_browser.contents, 'current')))
      There is no current release for this source package in Ubuntu.
 
 
@@ -475,7 +475,7 @@
     ...     version_headers = find_tags_by_class(
     ...         contents, 'boardCommentDetails')
     ...     for section in version_headers:
-    ...         print extract_text(section.div)
+    ...         print(extract_text(section.div))
 
     >>> anon_browser.open(
     ...     "http://launchpad.dev/ubuntu/+source/alsa-utils/+changelog";)
@@ -527,7 +527,7 @@
 'Overview' facet link.
 
     >>> anon_browser.getLink('Overview').click()
-    >>> print backslashreplace(anon_browser.title)
+    >>> print(backslashreplace(anon_browser.title))
     alsa-utils package : Ubuntu
 
 
@@ -543,7 +543,7 @@
 appropriate title and main heading, but preserving the distribution
 source package hierarchy.
 
-    >>> print backslashreplace(anon_browser.title)
+    >>> print(backslashreplace(anon_browser.title))
     Publishing history : alsa-utils package : Ubuntu
 
     >>> print_location(anon_browser.contents)
@@ -561,5 +561,5 @@
 the 'back' link at the bottom of the page.
 
     >>> anon_browser.getLink('back').click()
-    >>> print backslashreplace(anon_browser.title)
+    >>> print(backslashreplace(anon_browser.title))
     alsa-utils package : Ubuntu

=== modified file 'lib/lp/soyuz/stories/distroseries/add-architecture.txt'
--- lib/lp/soyuz/stories/distroseries/add-architecture.txt	2015-05-13 09:37:43 +0000
+++ lib/lp/soyuz/stories/distroseries/add-architecture.txt	2018-07-01 09:01:37 +0000
@@ -7,7 +7,7 @@
 
     >>> admin_browser.open('http://launchpad.dev/ubuntu/hoary')
     >>> admin_browser.getLink('Add architecture').click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Add a port of The Hoary...
 
 There is a cancel link.
@@ -23,7 +23,7 @@
     >>> admin_browser.getControl('Processor:').value = ['amd64']
     >>> admin_browser.getControl('Official Support').selected = True
     >>> admin_browser.getControl('Continue').click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     ia64 : Hoary (5.04) : Ubuntu
 
 Architecture tag is restricted to the usual Launchpad name format.

=== modified file 'lib/lp/soyuz/stories/ppa/xx-copy-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-copy-packages.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-copy-packages.txt	2018-07-01 09:01:37 +0000
@@ -58,7 +58,7 @@
     ...     'http://launchpad.dev/~cprov/+archive/ppa/+packages')
     >>> jblack_browser.getLink('Copy packages').click()
 
-    >>> print extract_text(find_main_content(jblack_browser.contents))
+    >>> print(extract_text(find_main_content(jblack_browser.contents)))
     Copy packages from PPA for Celso Providelo
     ...
     To be able to copy packages you have to participate in at least one
@@ -69,7 +69,7 @@
 provided link.
 
     >>> jblack_browser.getLink('Create a new PPA').click()
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     Activate PPA : James Blackwell
 
     >>> jblack_browser.getControl(
@@ -79,7 +79,7 @@
     ...     name="field.description").value = 'There we go ...'
     >>> jblack_browser.getControl("Activate").click()
 
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     PPA for James Blackwell : James Blackwell
 
 Now, that James has his own PPA, he navigates back to Celso's PPA
@@ -197,7 +197,7 @@
     >>> expander_url = jblack_browser.getLink(
     ...     id='pub%s-expander' % pmount_pub_id).url
     >>> jblack_extra_browser.open(expander_url)
-    >>> print extract_text(jblack_extra_browser.contents)
+    >>> print(extract_text(jblack_extra_browser.contents))
     Publishing details
       Published on 2007-07-09
       Copied from ubuntu hoary in Primary Archive for Ubuntu Linux
@@ -223,20 +223,20 @@
 Currently, James only has access to his just created PPA, which is the
 default form value for 'Destination PPA'.
 
-    >>> print jblack_browser.getControl('Destination PPA').displayOptions
+    >>> print(jblack_browser.getControl('Destination PPA').displayOptions)
     ['PPA for James Blackwell [~jblack/ubuntu/ppa]']
 
-    >>> print jblack_browser.getControl('Destination PPA').value
+    >>> print(jblack_browser.getControl('Destination PPA').value)
     ['~jblack/ubuntu/ppa']
 
 James notice that Celso's 'pmount' was uploaded and built in Warty,
 but he is using Hoary. No problem, because he can select a destination
 series while copying.
 
-    >>> print jblack_browser.getControl('Destination series').displayOptions
+    >>> print(jblack_browser.getControl('Destination series').displayOptions)
     ['The same series', 'Breezy Badger Autotest', 'Grumpy', 'Hoary', 'Warty']
 
-    >>> print jblack_browser.getControl('Destination series').value
+    >>> print(jblack_browser.getControl('Destination series').value)
     ['']
 
     >>> jblack_browser.getControl('Destination series').value = ['hoary']
@@ -268,7 +268,7 @@
 there is actually a newer version already available in hoary.
 
     >>> jblack_browser.getLink('PPA for James Blackwell').click()
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     Packages in “PPA for James Blackwell”...
 
     >>> print_ppa_packages(jblack_browser.contents)
@@ -285,7 +285,7 @@
     >>> expander_url = jblack_browser.getLink(
     ...     id='pub%s-expander' % pmount_pub_id).url
     >>> jblack_extra_browser.open(expander_url)
-    >>> print extract_text(jblack_extra_browser.contents)
+    >>> print(extract_text(jblack_extra_browser.contents))
     Publishing details
       Copied from ubuntu hoary in Primary Archive for Ubuntu Linux by James
       Blackwell
@@ -314,10 +314,10 @@
 build created during the copy is ready to be dispatched.
 
     >>> jblack_browser.getLink('i386').click()
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     i386 build of pmount 0.1-1 : PPA for James Blackwell : James Blackwell
 
-    >>> print extract_text(find_main_content(jblack_browser.contents))
+    >>> print(extract_text(find_main_content(jblack_browser.contents)))
     i386 build of pmount 0.1-1 in ubuntu hoary RELEASE
     PPA for James Blackwell i386 build of pmount 0.1-1
     created ...
@@ -354,11 +354,11 @@
 
     >>> jblack_browser.getLink('PPA for James Blackwell').click()
     >>> jblack_browser.getLink('View package details').click()
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     Packages in “PPA for James Blackwell”...
 
     >>> jblack_browser.getLink('Copy packages').click()
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     Copy packages from PPA for James Blackwell...
 
     >>> print_ppa_packages(jblack_browser.contents)
@@ -373,7 +373,7 @@
 
 Leave the Destination PPA alone, because it defaults to 'This PPA'.
 
-    >>> print jblack_browser.getControl('Destination PPA').displayValue
+    >>> print(jblack_browser.getControl('Destination PPA').displayValue)
     ['This PPA']
 
 The destination series always default to 'The same series'.
@@ -490,7 +490,7 @@
     >>> flush_database_updates()
 
     >>> for binary in pmount_binaries:
-    ...     print binary.displayname
+    ...     print(binary.displayname)
     pmount-bin 0.1-1 in hoary hppa
     pmount-bin 0.1-1 in hoary i386
 
@@ -592,7 +592,7 @@
 By default the copy view presents only PUBLISHED or PENDING packages.
 
     >>> jblack_browser.getLink('Copy packages').click()
-    >>> print jblack_browser.getControl(name='field.status_filter').value
+    >>> print(jblack_browser.getControl(name='field.status_filter').value)
     ['published']
 
     >>> print_ppa_packages(jblack_browser.contents)
@@ -687,7 +687,7 @@
     ...     name="field.description").value = 'Come friends ...'
     >>> jblack_browser.getControl("Activate").click()
 
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     PPA for James Blackwell Friends : “James Blackwell Friends” team
 
 PPA created, now James want to populate it with the finest packages he
@@ -709,7 +709,7 @@
 Now that James have access to more than one PPA, the copy-packages form
 allows him to select one of them.
 
-    >>> print jblack_browser.getControl('Destination PPA').displayOptions
+    >>> print(jblack_browser.getControl('Destination PPA').displayOptions)
     ['PPA for James Blackwell Friends [~jblack-friends/ubuntu/ppa]',
      'PPA for James Blackwell [~jblack/ubuntu/ppa]']
 
@@ -752,7 +752,7 @@
 sources and binaries are copied to their PPA.
 
     >>> jblack_browser.getLink('PPA for James Blackwell Friends').click()
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     Packages in “PPA for James Blackwell Friends”...
 
     >>> print_ppa_packages(jblack_browser.contents)
@@ -768,7 +768,7 @@
     >>> expander_url = jblack_browser.getLink(
     ...     id='pub%s-expander' % pmount_pub_id).url
     >>> jblack_extra_browser.open(expander_url)
-    >>> print extract_text(jblack_extra_browser.contents)
+    >>> print(extract_text(jblack_extra_browser.contents))
     Publishing details
     ...
     Built packages
@@ -779,7 +779,7 @@
     >>> expander_url = jblack_browser.getLink(
     ...     id='pub%s-expander' % iceweasel_pub_id).url
     >>> jblack_extra_browser.open(expander_url)
-    >>> print extract_text(jblack_extra_browser.contents)
+    >>> print(extract_text(jblack_extra_browser.contents))
     Publishing details
     ...
     Built packages
@@ -831,7 +831,7 @@
     ...     name="field.description").value = 'Come friends ...'
     >>> jblack_browser.getControl("Activate").click()
 
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     PPA for James Blackwell Sandbox : “James Blackwell Sandbox” team
 
 James now goes to his PPA and copy all sources to his Sandbox PPA
@@ -887,7 +887,7 @@
 even the 'warty' source, which was not denied.
 
     >>> jblack_browser.open('http://launchpad.dev/~jblack-sandbox/+archive')
-    >>> print jblack_browser.title
+    >>> print(jblack_browser.title)
     PPA for James Blackwell Sandbox : “James Blackwell Sandbox” team
 
     >>> print_ppa_packages(jblack_browser.contents)
@@ -910,10 +910,10 @@
     >>> jblack_browser.getControl(name='field.selected_sources').value = (
     ...     [deleted_pub_id])
 
-    >>> print jblack_browser.getControl('Destination PPA').displayValue
+    >>> print(jblack_browser.getControl('Destination PPA').displayValue)
     ['This PPA']
 
-    >>> print jblack_browser.getControl('Destination series').displayValue
+    >>> print(jblack_browser.getControl('Destination series').displayValue)
     ['The same series']
 
     >>> jblack_browser.getControl(

=== modified file 'lib/lp/soyuz/stories/ppa/xx-delete-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-delete-packages.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-delete-packages.txt	2018-07-01 09:01:37 +0000
@@ -42,13 +42,13 @@
     >>> cprov_browser.open(
     ...     'http://launchpad.dev/~cprov/+archive/ppa/+packages')
     >>> cprov_browser.getLink('Delete packages').click()
-    >>> print cprov_browser.title
+    >>> print(cprov_browser.title)
     Delete packages from PPA for Celso Providelo...
 
     >>> admin_browser.open(
     ...     'http://launchpad.dev/~cprov/+archive/ppa/+packages')
     >>> admin_browser.getLink('Delete packages').click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Delete packages from PPA for Celso Providelo...
 
 Once accessed the page provides a way to search for published package
@@ -168,7 +168,7 @@
     ...    name='field.selected_sources').value = ['100']
     Traceback (most recent call last):
     ...
-    ItemNotFoundError: insufficient items with name '100'
+    ItemNotFoundError: insufficient items with name u'100'
 
 An invalid value.
 
@@ -176,7 +176,7 @@
     ...    name='field.selected_sources').value = ['blah']
     Traceback (most recent call last):
     ...
-    ItemNotFoundError: insufficient items with name 'blah'
+    ItemNotFoundError: insufficient items with name u'blah'
 
 The deleted record is now presented accordingly in the +index page. We
 will use another browser to inspect the results of our deletions.
@@ -246,8 +246,8 @@
     >>> admin_browser.open(
     ...     'http://launchpad.dev/~cprov/+archive/ppa/+delete-packages')
     >>> main_content = find_main_content(admin_browser.contents)
-    >>> print extract_text(
-    ...   find_tags_by_class(str(main_content), 'top-portlet')[0])
+    >>> print(extract_text(
+    ...   find_tags_by_class(str(main_content), 'top-portlet')[0]))
     This PPA does not contain any source packages published.
 
 All the packages were deleted via the admin_browser, now we will
@@ -259,7 +259,7 @@
     >>> re_post_browser.getControl("Deletion comment").value = "DO IT AGAIN !"
     >>> re_post_browser.getControl("Request Deletion").click()
 
-    >>> print extract_text(find_main_content(re_post_browser.contents))
+    >>> print(extract_text(find_main_content(re_post_browser.contents)))
     Delete packages from PPA for Celso Providelo
     ...
     This PPA does not contain any source packages published.
@@ -293,10 +293,10 @@
 
     >>> admin_browser.open(
     ...    'http://launchpad.dev/~no-priv/+archive/ppa/+delete-packages')
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Delete packages from PPA for No Privileges Person...
 
-    >>> print extract_text(find_main_content(admin_browser.contents))
+    >>> print(extract_text(find_main_content(admin_browser.contents)))
     Delete packages from PPA for No Privileges Person
     ...
     This PPA does not contain any source packages published.
@@ -374,7 +374,7 @@
     >>> expander_url = user_browser.getLink(
     ...     id='pub%s-expander' % foo_pub_src.id).url
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
     Changelog
     Builds
@@ -389,7 +389,7 @@
 available for deletion because it contains a PUBLISHED binary.
 
     >>> user_browser.getLink('Delete packages').click()
-    >>> print user_browser.title
+    >>> print(user_browser.title)
     Delete packages from PPA for No Privileges Person...
 
     >>> print_ppa_packages(user_browser.contents)
@@ -400,7 +400,7 @@
     >>> expander_url = user_browser.getLink(
     ...     id='pub%s-expander' % foo_pub_src.id).url
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
     Changelog
     Builds
@@ -414,7 +414,7 @@
 The list of 'deletable' sources can be filtered by status. The default
 filter is 'Any Status', but the user can choose another.
 
-    >>> print user_browser.getControl(name='field.status_filter').value
+    >>> print(user_browser.getControl(name='field.status_filter').value)
     ['']
 
 When the user selects 'Published' filter and update the results, no
@@ -475,7 +475,7 @@
     >>> expander_url = user_browser.getLink(
     ...     id='pub%s-expander' % foo_pub_src.id).url
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
     Deleted ... ago by No Privileges Person
     Deletion of a number of base pairs that is not evenly divisible by three
@@ -516,7 +516,7 @@
     >>> expander_url = user_browser.getLink(
     ...     id='pub%s-expander' % foo_pub_src.id).url
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
     Removed from disk ... ago.
     Deleted ... ago by No Privileges Person
@@ -533,7 +533,7 @@
 
     >>> user_browser.open("http://launchpad.dev/ubuntu/+source/foobar/1.0";)
     >>> user_browser.getLink('See full publishing history').click()
-    >>> print extract_text(find_main_content(user_browser.contents))
+    >>> print(extract_text(find_main_content(user_browser.contents)))
     Publishing history of foobar 1.0 source package in Ubuntu
     ...
     1.0

=== modified file 'lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt'
--- lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-edit-dependencies.txt	2018-07-01 09:01:37 +0000
@@ -40,10 +40,10 @@
     >>> no_priv_browser.open("http://launchpad.dev/~no-priv/+archive/ubuntu/ppa";)
 
     >>> no_priv_browser.getLink('Edit PPA dependencies').click()
-    >>> print no_priv_browser.url
+    >>> print(no_priv_browser.url)
     http://launchpad.dev/~no-priv/+archive/ubuntu/ppa/+edit-dependencies
 
-    >>> print no_priv_browser.title
+    >>> print(no_priv_browser.title)
     Edit PPA dependencies : PPA for No Privileges Person :
     No Privileges Person
 
@@ -54,16 +54,16 @@
     ...     auth="Basic celso.providelo@xxxxxxxxxxxxx:test")
     >>> cprov_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
     >>> cprov_browser.getLink('Edit PPA dependencies').click()
-    >>> print cprov_browser.url
+    >>> print(cprov_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+edit-dependencies
-    >>> print cprov_browser.title
+    >>> print(cprov_browser.title)
     Edit PPA dependencies : PPA for Celso Providelo : Celso Providelo
 
     >>> admin_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
     >>> admin_browser.getLink('Edit PPA dependencies').click()
-    >>> print admin_browser.url
+    >>> print(admin_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+edit-dependencies
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Edit PPA dependencies : PPA for Celso Providelo : Celso Providelo
 
 Once accessed the page provides a way to remove recorded dependencies
@@ -72,10 +72,10 @@
     >>> def print_ppa_dependencies(contents):
     ...     empty_dep = find_tag_by_id(contents, 'empty-dependencies')
     ...     if empty_dep is not None:
-    ...         print extract_text(empty_dep)
+    ...         print(extract_text(empty_dep))
     ...     dependencies = find_tags_by_class(contents, 'ppa-dependencies')
     ...     for dep in dependencies:
-    ...         print extract_text(dep)
+    ...         print(extract_text(dep))
 
 When the 'Edit dependencies' page is loaded it will list all dependencies.
 
@@ -163,10 +163,10 @@
 
 The dependency entries are links to their target archives.
 
-    >>> print user_browser.getLink('PPA for Mark Shuttleworth').url
+    >>> print(user_browser.getLink('PPA for Mark Shuttleworth').url)
     http://launchpad.dev/~mark/+archive/ubuntu/ppa
 
-    >>> print user_browser.getLink('PPA for No Privileges Person').url
+    >>> print(user_browser.getLink('PPA for No Privileges Person').url)
     http://launchpad.dev/~no-priv/+archive/ubuntu/ppa
 
 If, by any chance, a dependency gets disabled, the link is turned off
@@ -192,7 +192,7 @@
     ...
     LinkNotFoundError
 
-    >>> print anon_browser.getLink('PPA for No Privileges Person').url
+    >>> print(anon_browser.getLink('PPA for No Privileges Person').url)
     http://launchpad.dev/~no-priv/+archive/ubuntu/ppa
 
 When accessed by their owners, a PPA depending on disabled archives
@@ -202,8 +202,8 @@
     >>> cprov_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
     >>> upload_hint = find_tag_by_id(
     ...     cprov_browser.contents, 'upload-hint')
-    >>> print extract_text(
-    ...     first_tag_by_class(str(upload_hint), 'message warning'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(str(upload_hint), 'message warning')))
     This PPA depends on disabled archives. it may cause spurious
     build failures or binaries with unexpected contents.
 
@@ -249,13 +249,13 @@
 add new dependencies.
 
     >>> user_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
-    >>> print find_tag_by_id(
-    ...     user_browser.contents, 'archive-dependencies')
+    >>> print(find_tag_by_id(
+    ...     user_browser.contents, 'archive-dependencies'))
     None
 
     >>> anon_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
-    >>> print find_tag_by_id(
-    ...     user_browser.contents, 'archive-dependencies')
+    >>> print(find_tag_by_id(
+    ...     user_browser.contents, 'archive-dependencies'))
     None
 
 We should also make sure that a user is unable to add a disabled PPA as a
@@ -534,7 +534,7 @@
 index page (see `IArchivedependency.title`).
 
     >>> admin_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     PPA for Celso Providelo : Celso Providelo
 
     >>> print_tag_with_id(admin_browser.contents, 'archive-dependencies')
@@ -558,7 +558,7 @@
     ...     ).selected = True
 
     >>> admin_browser.getLink("Cancel").click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     PPA for Celso Providelo : Celso Providelo
 
 The dependencies were not modified.

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-files.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-files.txt	2018-05-11 17:52:11 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-files.txt	2018-07-01 09:01:37 +0000
@@ -130,7 +130,7 @@
     ...         try:
     ...             found_url = browser.getLink(link).url
     ...         except LinkNotFoundError:
-    ...             print '%s: NOT FOUND' % libraryfile.filename
+    ...             print('%s: NOT FOUND' % libraryfile.filename)
     ...             continue
     ...         found_url = found_url.replace('%7E', '~')
     ...         if source_name is not None:
@@ -141,10 +141,10 @@
     ...             expected_url = '/'.join(
     ...                 (base_url, '+files', libraryfile.filename))
     ...         if found_url == expected_url:
-    ...             print '%s: OK' % libraryfile.filename
+    ...             print('%s: OK' % libraryfile.filename)
     ...         else:
-    ...             print '%s: NOT OK (%s != %s)' % (
-    ...                 libraryfile.filename, found_url, expected_url)
+    ...             print('%s: NOT OK (%s != %s)' % (
+    ...                 libraryfile.filename, found_url, expected_url))
 
 No Privileges user can access the files related with their PPA and its
 builds.
@@ -154,7 +154,7 @@
     >>> no_priv_browser.open(
     ...     "http://launchpad.dev/~no-priv/+archive/ubuntu/p3a/+packages";)
 
-    >>> print no_priv_browser.title
+    >>> print(no_priv_browser.title)
     Packages in “PPA named p3a for No Privileges Person”...
 
 Source changesfiles are served on the PPA '+files' traversal.
@@ -207,7 +207,7 @@
 '+files' traversal when the Build page is presented.
 
     >>> no_priv_browser.getLink('i386').click()
-    >>> print no_priv_browser.title
+    >>> print(no_priv_browser.title)
     i386 build of test-pkg 1.0 : PPA named p3a for No Privileges Person :
     No Privileges Person
 
@@ -250,10 +250,10 @@
 
 The 'No Privileges' user, the PPA owner, can download the DSC file.
 
-    >>> print http(r"""
+    >>> print(http(br"""
     ... GET %s HTTP/1.1
     ... Authorization: Basic no-priv@xxxxxxxxxxxxx:test
-    ... """ % (dsc_file_lp_url.replace('http://launchpad.dev', '')))
+    ... """ % (dsc_file_lp_url.replace('http://launchpad.dev', ''))))
     HTTP/1.1 303 See Other
     ...
     Location: https://...restricted.../test-pkg_1.0.dsc?token=...
@@ -270,10 +270,10 @@
     Traceback (most recent call last):
     ...
     Unauthorized
-    >>> print http(r"""
+    >>> print(http(br"""
     ... GET %s HTTP/1.1
     ... Authorization: Basic no-priv@xxxxxxxxxxxxx:test
-    ... """ % (deb_file_lp_url.replace('http://launchpad.dev', '')))
+    ... """ % (deb_file_lp_url.replace('http://launchpad.dev', ''))))
     HTTP/1.1 303 See Other
     ...
     Location: https://...restricted.../test-bin_1.0_all.deb?token=...
@@ -309,12 +309,12 @@
     >>> transaction.commit()
     >>> logout()
 
-    >>> print file_librarian_url
+    >>> print(file_librarian_url)
     http://.../test-pkg_1.0.dsc
 
-    >>> print http(r"""
+    >>> print(http(br"""
     ... GET %s HTTP/1.1
-    ... """ % file_lp_url.replace('http://launchpad.dev', ''))
+    ... """ % file_lp_url.replace('http://launchpad.dev', '')))
     HTTP/1.1 303 See Other
     ...
     Location: http://.../test-pkg_1.0.dsc
@@ -338,10 +338,10 @@
     >>> file_lp_url_without_ppa_name = (
     ...     'http://launchpad.dev/~no-priv/+archive/+files/test-pkg_1.0.dsc')
 
-    >>> print http(r"""
+    >>> print(http(br"""
     ... GET %s HTTP/1.1
     ... """ % file_lp_url_without_ppa_name.replace(
-    ...     'http://launchpad.dev', ''))
+    ...     'http://launchpad.dev', '')))
     HTTP/1.1 301 Moved Permanently
     ...
     Location: http://localhost/~no-priv/+archive/ubuntu/ppa/+files/test-pkg_1.0.dsc
@@ -351,10 +351,10 @@
 
     >>> buildlog_lp_url_without_ppa_name = (
     ...     'http://launchpad.dev/~no-priv/+archive/+build/1/+files/foo')
-    >>> print http(r"""
+    >>> print(http(br"""
     ... GET %s HTTP/1.1
     ... """ % buildlog_lp_url_without_ppa_name.replace(
-    ...     'http://launchpad.dev', ''))
+    ...     'http://launchpad.dev', '')))
     HTTP/1.1 301 Moved Permanently
     ...
     Location: http://.../~no-priv/+archive/ubuntu/ppa/+build/1/+files/...
@@ -381,7 +381,7 @@
 The librarian URL for a deleted file is None, by convention. See
 `ILibraryFileAlias` for more information on this.
 
-    >>> print dsc_file.http_url
+    >>> print(dsc_file.http_url)
     None
 
     >>> logout()
@@ -405,16 +405,16 @@
 If by any chance, mostly bookmarked URLs, it gets accessed via the
 LP proxy URL a proper NotFound error is raised.
 
-    >>> print file_lp_url
+    >>> print(file_lp_url)
     http://launchpad.dev/~no-priv/+archive/ubuntu/ppa/+sourcefiles/test-pkg/1.0/test-pkg_1.0.dsc
 
-    >>> not_found_file = http(r"""
+    >>> not_found_file = http(br"""
     ... GET %s HTTP/1.1
     ... """ % file_lp_url.replace('http://launchpad.dev', ''))
 
 It results in a 404 response.
 
-    >>> print not_found_file
+    >>> print(not_found_file)
     HTTP/1.1 404 Not Found
     ...
 
@@ -422,7 +422,7 @@
 the error occurred based on the traceback included in the page.
 
     >>> main_content = find_main_content(str(not_found_file))
-    >>> print extract_text(main_content)
+    >>> print(extract_text(main_content))
     Lost something?
     ...
     NotFound:

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-navigation.txt	2018-07-01 09:01:37 +0000
@@ -5,7 +5,7 @@
 that section is not present and there are no links to the ppa pages.
 
     >>> anon_browser.open('http://launchpad.dev/~matsubara')
-    >>> print find_tag_by_id(anon_browser.contents, 'ppas')
+    >>> print(find_tag_by_id(anon_browser.contents, 'ppas'))
     None
 
 Navigating to cprov's personal page:
@@ -25,7 +25,7 @@
     >>> anon_browser.url
     'http://launchpad.dev/~cprov/+archive/ubuntu/ppa'
 
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     PPA for Celso Providelo : Celso Providelo
 
 There is a link that allows users with permission to create a PPA.
@@ -35,14 +35,14 @@
     >>> cprov_browser.open("http://launchpad.dev/~cprov";)
     >>> cprov_browser.getLink('Launchpad Buildd Admins').click()
 
-    >>> print cprov_browser.title
+    >>> print(cprov_browser.title)
     Launchpad Buildd Admins in Launchpad
 
     >>> print_tag_with_id(cprov_browser.contents, 'ppas')
     Personal package archives
     Create a new PPA
 
-    >>> print cprov_browser.getLink('Create a new PPA').url
+    >>> print(cprov_browser.getLink('Create a new PPA').url)
     http://launchpad.dev/~launchpad-buildd-admins/+activate-ppa
 
 While navigating around the PPA the "structural location" includes a PPA:
@@ -84,17 +84,17 @@
 the packages listing portlet.
 
     >>> packages_portlet = find_tag_by_id(anon_browser.contents, 'packages')
-    >>> print packages_portlet.find('a').string
+    >>> print(packages_portlet.find('a').string)
     View package details
     >>> anon_browser.getLink('View package details').click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Packages in...
 
 You can see the build details of the packages in the archive by using
 the 'View all builds' link.
 
     >>> anon_browser.getLink('View all builds').click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : Default PPA : Celso Providelo
 
     >>> anon_browser.url
@@ -102,7 +102,7 @@
 
 The user could return to the 'PPA' overview by using the breadcrumb link.
 
-    >>> print anon_browser.getLink('Default PPA').url
+    >>> print(anon_browser.getLink('Default PPA').url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa
 
 The user can navigate to an individual build details:
@@ -110,7 +110,7 @@
     >>> anon_browser.getControl('All states').click()
     >>> anon_browser.getControl('Filter').click()
     >>> anon_browser.getLink('i386 build of iceweasel').click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     i386 build of iceweasel 1.0 : Default PPA : Celso Providelo
 
     >>> print_location(anon_browser.contents)

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt	2018-05-04 21:59:32 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt	2018-07-01 09:01:37 +0000
@@ -6,10 +6,10 @@
 
     >>> anon_browser.open('http://launchpad.dev/~cprov/+archive/ubuntu/ppa')
     >>> anon_browser.getLink('View package details').click()
-    >>> print anon_browser.url
+    >>> print(anon_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+packages
 
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Packages in...
 
 
@@ -34,10 +34,10 @@
 the 'View all builds' link.
 
     >>> anon_browser.getLink('View all builds').click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : PPA for Celso Providelo : Celso Providelo
 
-    >>> print anon_browser.url
+    >>> print(anon_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+builds
 
 The rest of the builds page functionality is tested generically at
@@ -54,7 +54,7 @@
     ...     'http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+packages')
     >>> package_totals = find_portlet(
     ...     anon_browser.contents, "Package totals")
-    >>> print extract_text(package_totals)
+    >>> print(extract_text(package_totals))
     Package totals
     ...
     Package counters and estimated archive size temporarily
@@ -68,7 +68,7 @@
 
     >>> build_summary = find_portlet(
     ...     anon_browser.contents, "View all builds Package build summary")
-    >>> print extract_text(build_summary)
+    >>> print(extract_text(build_summary))
     View...
     A total of 4 builds have been created for this PPA.
     Completed builds
@@ -78,7 +78,7 @@
 Successful builds link directly to the builds filter.
 
     >>> successful_builds_link = anon_browser.getLink('3 successful')
-    >>> print successful_builds_link.url
+    >>> print(successful_builds_link.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+builds?build_state=built
 
 
@@ -89,7 +89,7 @@
     ...     package_table = find_tag_by_id(
     ...         contents, 'packages_list')
     ...     for ppa_row in package_table.findChildren('tr'):
-    ...         print extract_text(ppa_row)
+    ...         print(extract_text(ppa_row))
 
     >>> print_archive_package_rows(anon_browser.contents)
     Source              Published   Status     Series      Section  Build
@@ -107,7 +107,7 @@
 
     >>> expander_url = anon_browser.getLink(id='pub29-expander').url
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
       Published on 2007-07-09
       Copied from ubuntu hoary in Primary Archive for Ubuntu Linux
@@ -138,7 +138,7 @@
     >>> cprov_ppa = cprov.archive
     >>> pmount_i386_pub = cprov_ppa.getAllPublishedBinaries(
     ...     name=u'pmount', version='0.1-1')[1]
-    >>> print pmount_i386_pub.displayname
+    >>> print(pmount_i386_pub.displayname)
     pmount 0.1-1 in warty i386
     >>> from lp.soyuz.enums import PackagePublishingStatus
     >>> naked_pub = removeSecurityProxy(pmount_i386_pub)
@@ -149,7 +149,7 @@
 
     # Now, to re-display the pmount expanded section:
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Note: Some binary packages for this source are not yet published in the
     repository.
     Publishing details
@@ -214,7 +214,7 @@
     ...     "http://launchpad.dev/~joe/+archive/ubuntu/ppa/+packages";)
     >>> expander_url = cprov_browser.getLink(id=expander_link_id).url
     >>> cprov_browser.open(expander_url)
-    >>> print cprov_browser.getLink("PPA named p3a for Celso Providelo").url
+    >>> print(cprov_browser.getLink("PPA named p3a for Celso Providelo").url)
     http://launchpad.dev/~cprov/+archive/ubuntu/p3a
 
 But Joe himself will not see the link.
@@ -382,7 +382,7 @@
     ...     foo_browser.contents, 'expander')[0]['id']
     >>> expander_url = foo_browser.getLink(id=expander_id).url
     >>> anon_browser.open(expander_url)
-    >>> print anon_browser.getLink("orig").url
+    >>> print(anon_browser.getLink("orig").url)
     http://.../+sourcefiles/.../foo.orig.tar.gz
 
 The uploader name is linkified to that user's home page:
@@ -416,7 +416,7 @@
     >>> def print_build_status(contents):
     ...     rows = find_tags_by_class(contents, 'archive_package_row')
     ...     headers = rows[0].findAll('th')
-    ...     print extract_text(headers[0]), extract_text(headers[-1])
+    ...     print(extract_text(headers[0]), extract_text(headers[-1]))
     ...     for row in rows[1:]:
     ...         columns = row.findAll('td')
     ...         name = extract_text(columns[0])
@@ -424,7 +424,7 @@
     ...         built_text = columns[-1].a
     ...         if built_text is not None:
     ...             built_text = built_text.renderContents()
-    ...         print name, built_icon, built_text
+    ...         print(name, built_icon, built_text)
 
     >>> print_build_status(anon_browser.contents)
     Source                    Build Status
@@ -438,7 +438,7 @@
 
     >>> anon_browser.getLink('i386').click()
 
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     i386 build of cdrkit 1.0 : PPA for Celso Providelo : Celso Providelo
 
 This feature is also useful from the PPA owner perspective. When Celso
@@ -467,5 +467,5 @@
 corresponding build page.
 
     >>> anon_browser.getLink('i386').click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     i386 build of cdrkit 1.0 : PPA for Celso Providelo : Celso Providelo

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-private-teams.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-private-teams.txt	2013-09-27 04:13:23 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-private-teams.txt	2018-07-01 09:01:37 +0000
@@ -35,7 +35,7 @@
 The form looks almost identical to that for a public team.
 
    >>> browser.getLink('Create a new PPA').click()
-   >>> print browser.title
+   >>> print(browser.title)
    Activate PPA : ...Private Team...
 
 There is, however, an extra bit of information indicating the new PPA
@@ -46,8 +46,8 @@
 
 The URL template also shows the private URL.
 
-    >>> print extract_text(
-    ...     first_tag_by_class(browser.contents, 'form'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(browser.contents, 'form')))
     URL:
       http://private-ppa.launchpad.dev/private-team/
       At least one lowercase letter or number, followed by letters, numbers,
@@ -58,7 +58,7 @@
    >>> browser.getControl(name='field.displayname').value = "Private Team PPA"
    >>> browser.getControl(name='field.accepted').value = True
    >>> browser.getControl("Activate").click()
-   >>> print browser.title
+   >>> print(browser.title)
    Private Team PPA : “Private Team” team
 
 

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt	2018-07-01 09:01:37 +0000
@@ -21,7 +21,7 @@
 
     >>> anon_browser.getLink("PPA for Celso Providelo").click()
 
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     PPA for Celso Providelo : Celso Providelo
 
 On the other hand, Sample Person hasn't activated their PPA yet, so they
@@ -36,7 +36,7 @@
 
     >>> sample_browser.getLink("Create a new PPA").click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Activate PPA : Sample Person
 
 This page presents a pointer to the current PPA-ToS (terms of service)
@@ -50,8 +50,8 @@
 warned about the fact that if the user has any PPAs with published
 packages then they will not be able to rename their account.
 
-    >>> print extract_text(
-    ...     first_tag_by_class(sample_browser.contents, 'actions'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(sample_browser.contents, 'actions')))
     A PPA's URL cannot be changed once it has had packages
     published. You will not be able to rename Sample Person (name12)
     until all such PPAs are deleted.
@@ -60,7 +60,7 @@
 'PPA name' and 'Displayname' are required fields.  For the first activated
 PPA, the name is pre-filled with a suggestion of "ppa":
 
-    >>> print sample_browser.getControl(name="field.name").value
+    >>> print(sample_browser.getControl(name="field.name").value)
     ppa
 
     >>> sample_browser.getControl("Activate").click()
@@ -91,13 +91,13 @@
 
 A successful activation redirects to the PPA page
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Sample PPA : Sample Person
 
 Where Sample person user can see the description previously entered.
 
-    >>> print extract_text(
-    ...     find_tag_by_id(sample_browser.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(sample_browser.contents, 'edit-description')))
     Edit PPA description
     Hoohay for PPA.
 
@@ -115,11 +115,11 @@
 After confirming the changes Sample Person is sent to the PPA page
 where they can see the updated information.
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Sample testing PPA : Sample Person
 
-    >>> print extract_text(
-    ...     find_tag_by_id(sample_browser.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(sample_browser.contents, 'edit-description')))
     Edit PPA description
     Howdy, cowboys !
 
@@ -129,10 +129,10 @@
     >>> sample_browser.getControl(name="field.description").value = ('')
     >>> sample_browser.getControl("Save").click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Sample testing PPA : Sample Person
 
-    >>> print find_tag_by_id(sample_browser.contents, 'description')
+    >>> print(find_tag_by_id(sample_browser.contents, 'description'))
     None
 
 On the other hand, the PPA 'displayname' field is required. Sample
@@ -142,7 +142,7 @@
     >>> sample_browser.getControl(name="field.displayname").value = ('')
     >>> sample_browser.getControl("Save").click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Change details : Sample testing PPA...
 
     >>> print_feedback_messages(sample_browser.contents)
@@ -160,7 +160,7 @@
     ...     auth="Basic celso.providelo@xxxxxxxxxxxxx:test")
     >>> cprov_browser.open("http://launchpad.dev/~landscape-developers";)
 
-    >>> print find_tag_by_id(cprov_browser.contents, 'ppas')
+    >>> print(find_tag_by_id(cprov_browser.contents, 'ppas'))
     None
 
 Even if we try the URL directly:
@@ -181,7 +181,7 @@
 
     >>> sample_browser.getLink('Create a new PPA').click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Activate PPA : ...
 
     >>> sample_browser.getControl(
@@ -195,8 +195,8 @@
 The user is, again, warned about the fact that activating this PPA
 will block renaming of the context team.
 
-    >>> print extract_text(
-    ...     first_tag_by_class(sample_browser.contents, 'actions'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(sample_browser.contents, 'actions')))
     A PPA's URL cannot be changed once it has had packages
     published. You will not be able to rename Landscape Developers
     (landscape-developers) until all such PPAs are deleted.
@@ -206,11 +206,11 @@
 
     >>> sample_browser.getControl("Activate").click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Devel PPA : “Landscape Developers” team
 
-    >>> print extract_text(
-    ...     find_tag_by_id(sample_browser.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(sample_browser.contents, 'edit-description')))
     Edit PPA description
     Hoohay for Team PPA.
 
@@ -226,11 +226,11 @@
     ...    'Yay, I can change it.')
     >>> sample_browser.getControl("Save").click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Devel PPA : “Landscape Developers” team
 
-    >>> print extract_text(
-    ...     find_tag_by_id(sample_browser.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(sample_browser.contents, 'edit-description')))
     Edit PPA description
     Yay, I can change it.
 
@@ -242,11 +242,11 @@
     ...    'Discarded ...')
     >>> sample_browser.getLink("Cancel").click()
 
-    >>> print sample_browser.title
+    >>> print(sample_browser.title)
     Devel PPA : “Landscape Developers” team
 
-    >>> print extract_text(
-    ...     find_tag_by_id(sample_browser.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(sample_browser.contents, 'edit-description')))
     Edit PPA description
     Yay, I can change it.
 
@@ -269,10 +269,10 @@
     >>> sample_browser.getControl(name='field.name').value = 'duderinos'
     Traceback (most recent call last):
     ...
-    LookupError: name 'field.name'
+    LookupError: name u'field.name'
 
-    >>> print extract_text(
-    ...     first_tag_by_class(sample_browser.contents, 'form'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(sample_browser.contents, 'form')))
     Name: landscape-developers
     This team has an active PPA with packages published and may not be renamed.
     ...
@@ -298,11 +298,11 @@
     ...    'Go for it, you lazy !')
     >>> admin_browser.getControl("Activate").click()
 
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Hack PPA : James Blackwell
 
-    >>> print extract_text(
-    ...     find_tag_by_id(admin_browser.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(admin_browser.contents, 'edit-description')))
     Edit PPA description
     Go for it, you lazy !
 
@@ -317,14 +317,14 @@
 virtualisation settings, and so on.
 
     >>> sample_browser.open("http://launchpad.dev/~jblack/+archive";)
-    >>> print sample_browser.getLink("Administer archive")
+    >>> print(sample_browser.getLink("Administer archive"))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
     >>> admin_browser.open("http://launchpad.dev/~jblack/+archive";)
     >>> admin_browser.getLink("Administer archive").click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Administer : Hack PPA...
 
     >>> commercial_browser = setupBrowser(
@@ -400,7 +400,7 @@
 Once confirmed the administrator is sent to the PPA page where they can
 see some of the updated information.
 
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Hack PPA : James Blackwell
 
     >>> print_feedback_messages(admin_browser.contents)
@@ -440,8 +440,8 @@
     >>> admin_browser.getControl("Save").click()
 
     >>> admin_browser.getLink("Administer archive").click()
-    >>> print admin_browser.getControl(
-    ...     name="field.authorized_size").value
+    >>> print(admin_browser.getControl(
+    ...     name="field.authorized_size").value)
     2147483647
 
 Submitting the form with an authorized_size value that is too large
@@ -460,13 +460,13 @@
 
     >>> admin_browser.getLink("Cancel").click()
 
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Hack PPA : James Blackwell
 
     >>> admin_browser.getLink("Administer archive").click()
     >>> admin_browser.getLink("Cancel").click()
 
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Hack PPA : James Blackwell
 
 
@@ -504,11 +504,11 @@
 
     >>> browser1.getControl("Activate").click()
 
-    >>> print browser1.title
+    >>> print(browser1.title)
     Boom PPA : Foo Bar
 
-    >>> print extract_text(
-    ...     find_tag_by_id(browser1.contents, 'edit-description'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(browser1.contents, 'edit-description')))
     Edit PPA description
     PPA rocks!
 
@@ -521,7 +521,7 @@
     There is 1 error.
     You already have a PPA for Ubuntu named &#x27;boomppa&#x27;.
 
-    >>> print browser2.getControl(name="field.name").value
+    >>> print(browser2.getControl(name="field.name").value)
     boomppa
 
 
@@ -546,7 +546,7 @@
 
     >>> cprov_browser.getLink("Create a new PPA").click()
 
-    >>> print cprov_browser.title
+    >>> print(cprov_browser.title)
     Activate PPA : Celso Providelo
 
     >>> print_tag_with_id(cprov_browser.contents, 'ppas')
@@ -556,10 +556,10 @@
     >>> cprov_browser.getControl(name="field.accepted")
     Traceback (most recent call last):
     ...
-    LookupError: name 'field.accepted'
+    LookupError: name u'field.accepted'
 
-    >>> print extract_text(
-    ...     first_tag_by_class(cprov_browser.contents, 'form'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(cprov_browser.contents, 'form')))
     URL:
       http://ppa.launchpad.dev/cprov/
       At least one lowercase letter or number, followed by letters, numbers,
@@ -573,7 +573,7 @@
 The 'PPA name' field is not pre-filled and if Celso does not fill it then
 an error is raised.
 
-    >>> print cprov_browser.getControl(name="field.name").value
+    >>> print(cprov_browser.getControl(name="field.name").value)
     <BLANKLINE>
 
     >>> cprov_browser.getControl(
@@ -622,7 +622,7 @@
     >>> cprov_browser.getControl(name="field.name").value = 'edge'
     >>> cprov_browser.getControl("Activate").click()
 
-    >>> print cprov_browser.title
+    >>> print(cprov_browser.title)
     Edge PPA : Celso Providelo
 
 Back to his profile page Celso and anyone can his multiple PPAs.
@@ -667,7 +667,7 @@
     PPA for Celso Providelo
     Create a new PPA
 
-    >>> print cprov_browser.getLink('Edge PPA')
+    >>> print(cprov_browser.getLink('Edge PPA'))
     <Link ...>
 
 And direct access to the PPA page is also denied.
@@ -710,9 +710,9 @@
 
 Initially, the PPA is enabled and publishes.
 
-    >>> print no_priv_browser.getControl(name='field.enabled').value
+    >>> print(no_priv_browser.getControl(name='field.enabled').value)
     True
-    >>> print no_priv_browser.getControl(name='field.publish').value
+    >>> print(no_priv_browser.getControl(name='field.publish').value)
     True
 
 After disabling the PPA a warning message is displayed on its page.
@@ -720,20 +720,20 @@
     >>> no_priv_browser.getControl(name='field.enabled').value = False
     >>> no_priv_browser.getControl(name='field.publish').value = False
     >>> no_priv_browser.getControl('Save').click()
-    >>> print extract_text(
-    ...     first_tag_by_class(no_priv_browser.contents, 'warning message'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(no_priv_browser.contents, 'warning message')))
     This PPA has been disabled.
 
 Going back to the edit page, we can see the publish flag was cleared:
 
     >>> no_priv_browser.open(
     ...     "http://launchpad.dev/~no-priv/+archive/ppa/+edit";)
-    >>> print no_priv_browser.getControl(name='field.publish').value
+    >>> print(no_priv_browser.getControl(name='field.publish').value)
     False
 
 Once we re-enable the PPA the "disabled" warning message will be gone.
 
-    >>> print no_priv_browser.getControl(name='field.enabled').value
+    >>> print(no_priv_browser.getControl(name='field.enabled').value)
     False
 
     >>> no_priv_browser.getControl(name='field.enabled').value = True
@@ -750,7 +750,7 @@
 navigation menu.
 
     >>> anon_browser.open("http://launchpad.dev/~no-priv/+archive/ppa";)
-    >>> print anon_browser.getLink("Delete PPA")
+    >>> print(anon_browser.getLink("Delete PPA"))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
@@ -760,13 +760,13 @@
 
 Clicking this link takes the user to a page that allows deletion of a PPA:
 
-    >>> print no_priv_browser.title
+    >>> print(no_priv_browser.title)
     Delete “PPA for No Privileges Person” : PPA for No Privileges Person :
         No Privileges Person
 
 The page contains a stern warning that this action is final and irreversible:
 
-    >>> print extract_text(find_main_content(no_priv_browser.contents))
+    >>> print(extract_text(find_main_content(no_priv_browser.contents)))
     Delete “PPA for No Privileges Person”
     ...
     Deleting a PPA will destroy all of its packages, files and the
@@ -778,7 +778,7 @@
 If the user changes their mind, they can click on the cancel link to go back
 a page:
 
-    >>> print no_priv_browser.getLink("Cancel").url
+    >>> print(no_priv_browser.getLink("Cancel").url)
     http://launchpad.dev/~no-priv/+archive/ubuntu/ppa
 
 Otherwise, they have a button to press to confirm the deletion.
@@ -788,7 +788,7 @@
 This action will redirect the user back to their profile page, which will
 contain a notification message that the deletion is in progress.
 
-    >>> print no_priv_browser.url
+    >>> print(no_priv_browser.url)
     http://launchpad.dev/~no-priv
 
     >>> print_feedback_messages(no_priv_browser.contents)
@@ -803,18 +803,18 @@
 However, most of the action links are removed for deleted PPAs, so you can
 no longer "Delete packages", "Edit PPA dependencies", or "Change details".
 
-    >>> print no_priv_browser.getLink("Change details")
+    >>> print(no_priv_browser.getLink("Change details"))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
-    >>> print no_priv_browser.getLink("Edit PPA dependencies")
+    >>> print(no_priv_browser.getLink("Edit PPA dependencies"))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
     >>> no_priv_browser.getLink("View package details").click()
-    >>> print no_priv_browser.getLink("Delete packages")
+    >>> print(no_priv_browser.getLink("Delete packages"))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
@@ -835,6 +835,6 @@
     >>> "Deleted PPAs can&#x27;t be enabled." in no_priv_browser.contents
     True
     >>> no_priv_browser.getLink('Cancel').click()
-    >>> print extract_text(
-    ...     first_tag_by_class(no_priv_browser.contents, 'warning message'))
+    >>> print(extract_text(
+    ...     first_tag_by_class(no_priv_browser.contents, 'warning message')))
     This PPA has been deleted.

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscription-stories.txt	2018-07-01 09:01:37 +0000
@@ -19,7 +19,7 @@
     ...     subscriptions = find_tags_by_class(contents,
     ...                                        'archive-subscription-row')
     ...     for subscription in subscriptions:
-    ...         print extract_text(subscription)
+    ...         print(extract_text(subscription))
 
 == Story: An archive owner can add a subscription for a private archive ==
 
@@ -72,7 +72,7 @@
     ...     subscriptions = find_tags_by_class(contents,
     ...                                        'archive_subscriber_row')
     ...     for subscription in subscriptions:
-    ...         print extract_text(subscription)
+    ...         print(extract_text(subscription))
 
     >>> print_archive_subscriptions(cprov_browser.contents)
     Name                Expires     Comment
@@ -144,7 +144,7 @@
 subscription for the launchpad team is displayed as well as a notification
 about the update.
 
-    >>> print cprov_browser.url
+    >>> print(cprov_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+subscriptions
     >>> print_archive_subscriptions(cprov_browser.contents)
     Name                    Expires       Comment
@@ -174,7 +174,7 @@
 subscription is no longer displayed and a notification about the
 cancellation is displayed.
 
-    >>> print cprov_browser.url
+    >>> print(cprov_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+subscriptions
     >>> print_archive_subscriptions(cprov_browser.contents)
     Name                    Expires       Comment

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt	2016-03-03 18:41:27 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppa-subscriptions.txt	2018-07-01 09:01:37 +0000
@@ -21,7 +21,7 @@
     >>> cprov_browser.open(
     ...     "http://launchpad.dev/~cprov/+archive/ppa/+subscriptions";)
 
-    >>> print cprov_browser.url
+    >>> print(cprov_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa
 
     >>> print_feedback_messages(cprov_browser.contents)
@@ -52,11 +52,11 @@
 with a message:
 
     >>> main_content = find_main_content(cprov_browser.contents)
-    >>> print extract_text(main_content.find('h1'))
+    >>> print(extract_text(main_content.find('h1')))
     Manage access to PPA named p3a for Celso Providelo
 
-    >>> print extract_text(find_tag_by_id(cprov_browser.contents,
-    ...     'no-subscribers'))
+    >>> print(extract_text(find_tag_by_id(cprov_browser.contents,
+    ...     'no-subscribers')))
     No one has access to install software from this PPA.
 
 Create two new users that can be subscribed to archives, and a team:
@@ -89,7 +89,7 @@
 
     >>> for row in find_tags_by_class(cprov_browser.contents,
     ...                               'archive_subscriber_row'):
-    ...     print extract_text(row)
+    ...     print(extract_text(row))
     Name                Expires     Comment
     Brad Smith          2200-08-01  Brad can access for a while.  Edit/Cancel
     Team Joe                        Joes friends are my friends   Edit/Cancel
@@ -101,7 +101,7 @@
 heading:
 
     >>> cprov_browser.open("/~cprov/+archivesubscriptions")
-    >>> print find_main_content(cprov_browser.contents)
+    >>> print(find_main_content(cprov_browser.contents))
     <div...
     <h1>Private PPA access</h1>...
 
@@ -109,7 +109,7 @@
 explanation if they try to view their archive subscriptions:
 
     >>> explanation = find_main_content(cprov_browser.contents).find('p')
-    >>> print extract_text(explanation)
+    >>> print(extract_text(explanation))
     You do not have any current subscriptions to private archives...
 
 First, create a subscription for Joe Smith's team to mark's archive
@@ -131,7 +131,7 @@
     ...     "http://launchpad.dev/~joesmith/+archivesubscriptions";)
     >>> for row in find_tags_by_class(joe_browser.contents,
     ...                               'archive-subscription-row'):
-    ...     print extract_text(row)
+    ...     print(extract_text(row))
     Archive                          Owner
     PPA named... (ppa:mark/p3a)      Mark Shuttleworth       View
     PPA named... (ppa:cprov/p3a)     Celso Providelo         View
@@ -207,7 +207,7 @@
     >>> rows = find_tags_by_class(
     ...     joe_browser.contents, 'archive-subscription-row')
     >>> for row in rows:
-    ...     print extract_text(row)
+    ...     print(extract_text(row))
     Archive                                            Owner
     PPA named p3a for Mark Shuttleworth (ppa:mark/p3a) Mark Shuttleworth View
     PPA named p3a for Celso Providelo (ppa:cprov/p3a)  Celso Providelo   View

=== modified file 'lib/lp/soyuz/stories/ppa/xx-private-ppas.txt'
--- lib/lp/soyuz/stories/ppa/xx-private-ppas.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/ppa/xx-private-ppas.txt	2018-07-01 09:01:37 +0000
@@ -11,7 +11,7 @@
     >>> def list_ppas_in_browser_page(browser):
     ...     ppas = [extract_text(ppa_row) for ppa_row in find_tags_by_class(
     ...         browser.contents, 'ppa_batch_row')]
-    ...     print "\n".join(ppas)
+    ...     print("\n".join(ppas))
 
 
 == Set Up a Private PPA ==
@@ -121,7 +121,7 @@
 
     >>> cprov_browser.getLink("PPA named p3a for Celso Providelo").click()
 
-    >>> print cprov_browser.title
+    >>> print(cprov_browser.title)
     PPA named p3a for Celso Providelo : Celso Providelo
 
 When a non-privileged user browses to a profile page for a person or
@@ -129,7 +129,7 @@
 link to the PPA page is not present.
 
     >>> browser.open("http://launchpad.dev/~landscape-developers";)
-    >>> print find_tag_by_id(browser.contents, 'ppas')
+    >>> print(find_tag_by_id(browser.contents, 'ppas'))
     None
 
     >>> browser.getLink("PPA for Landscape Developers").click()

=== modified file 'lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt'
--- lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt	2018-01-26 22:18:38 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt	2018-07-01 09:01:37 +0000
@@ -23,9 +23,9 @@
 published) and a list of context series and corresponding architectures
 supported for PPA.
 
-    >>> print extract_text(
+    >>> print(extract_text(
     ...    find_tag_by_id(anon_browser.contents,
-    ...                   'supports_virtualized_architectures'))
+    ...                   'supports_virtualized_architectures')))
     PPA supported series
     Hoary (5.04) - development i386 (official)
     Warty (4.10) - current i386 (official)
@@ -33,8 +33,8 @@
 Up to 5 latest source publications are also presented in the 'Latest
 sources' section.
 
-    >>> print extract_text(
-    ...    find_tag_by_id(anon_browser.contents, 'ppa_latest_uploads'))
+    >>> print(extract_text(
+    ...    find_tag_by_id(anon_browser.contents, 'ppa_latest_uploads')))
     Latest uploads
     cdrkit 1.0 in breezy-autotest  in PPA for Celso Providelo ... ago
     iceweasel 1.0 in breezy-autotest in PPA for Mark Shuttleworth ... ago
@@ -44,8 +44,8 @@
 The 5 most active PPAs are listed in the 'Most active' section. Since
 we only have 3 PPAs in sampledata they are all presented.
 
-    >>> print extract_text(
-    ...    find_tag_by_id(anon_browser.contents, 'ppa_most_active'))
+    >>> print(extract_text(
+    ...    find_tag_by_id(anon_browser.contents, 'ppa_most_active')))
     Most active
     PPAs with the highest number of uploads in the last 7 days.
 
@@ -78,26 +78,26 @@
 
     >>> anon_browser.open("http://launchpad.dev/debian";)
     >>> anon_browser.getLink("Personal Package Archives").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Personal Package Archives : Debian
 
 PPA supported architectures reflects what we have in sampledata.
 
-    >>> print extract_text(
+    >>> print(extract_text(
     ...    find_tag_by_id(anon_browser.contents,
-    ...                   'supports_virtualized_architectures'))
+    ...                   'supports_virtualized_architectures')))
     PPA supported series
     Sarge (3.1) - frozen
     Woody (3.0) - current i386 (official)
 
 'Latest uploads' section is not presented.
 
-    >>> print find_tag_by_id(anon_browser.contents, 'ppa_latest_uploads')
+    >>> print(find_tag_by_id(anon_browser.contents, 'ppa_latest_uploads'))
     None
 
 'Most active' section is not presented.
 
-    >>> print find_tag_by_id(anon_browser.contents, 'ppa_most_active')
+    >>> print(find_tag_by_id(anon_browser.contents, 'ppa_most_active'))
     None
 
 The 'search' form is also suppresed.
@@ -105,7 +105,7 @@
     >>> anon_browser.getControl('Search', index=0).click()
     Traceback (most recent call last):
     ...
-    LookupError: label 'Search'
+    LookupError: label u'Search'
 
 
 == Searching PPAs ==
@@ -121,7 +121,7 @@
     >>> anon_browser.getControl('Search', index=0).click()
     >>> for ppa_row in find_tags_by_class(
     ...     anon_browser.contents, 'ppa_batch_row'):
-    ...     print extract_text(ppa_row)
+    ...     print(extract_text(ppa_row))
     PPA for Celso Providelo
     packages to help my friends.
     3
@@ -133,14 +133,14 @@
 
 When a search is requested the information sections are not rendered.
 
-    >>> print find_tag_by_id(anon_browser.contents, 'ppa_most_active')
-    None
-
-    >>> print find_tag_by_id(anon_browser.contents, 'ppa_latest_uploads')
-    None
-
-    >>> print find_tag_by_id(
-    ...     anon_browser.contents, 'supports_virtualized_architectures')
+    >>> print(find_tag_by_id(anon_browser.contents, 'ppa_most_active'))
+    None
+
+    >>> print(find_tag_by_id(anon_browser.contents, 'ppa_latest_uploads'))
+    None
+
+    >>> print(find_tag_by_id(
+    ...     anon_browser.contents, 'supports_virtualized_architectures'))
     None
 
 The information section will be only rendered if the page is reloaded
@@ -171,7 +171,7 @@
 
     >>> for ppa_row in find_tags_by_class(anon_browser.contents,
     ...                                   'ppa_batch_row'):
-    ...    print extract_text(ppa_row)
+    ...    print(extract_text(ppa_row))
     PPA for Celso Providelo
     packages to help my friends.
     3
@@ -228,7 +228,7 @@
     ...     "http://launchpad.dev/ubuntu/+ppas";
     ...     "?name_filter=packages&name_filter=friends")
     >>> [row] = find_tags_by_class(anon_browser.contents, 'ppa_batch_row')
-    >>> print extract_text(row)
+    >>> print(extract_text(row))
     PPA for Celso Providelo...
 
 
@@ -253,7 +253,7 @@
 'displayname' is a link to its corresponding page:
 
     >>> anon_browser.getLink("PPA for Celso Providelo").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     PPA for Celso Providelo : Celso Providelo
 
 The first portlet in the PPA index page tell users how to install the
@@ -262,7 +262,7 @@
     >>> install_portlet = find_portlet(
     ...     anon_browser.contents, 'Adding this PPA to your system')
 
-    >>> print extract_text(install_portlet)
+    >>> print(extract_text(install_portlet))
     Adding this PPA to your system
     You can update your system with unsupported packages from this
     untrusted PPA by adding ppa:cprov/ppa to your system's Software Sources.
@@ -278,12 +278,12 @@
 wiki, which contains more documentation about the PPA installation
 procedure.
 
-    >>> print anon_browser.getLink('Read about installing').url
+    >>> print(anon_browser.getLink('Read about installing').url)
     http://launchpad.dev/+help-soyuz/ppa-sources-list.html
 
 The PPA owner reference is a link to its profile page.
 
-    >>> print anon_browser.getLink('Celso Providelo').url
+    >>> print(anon_browser.getLink('Celso Providelo').url)
     http://launchpad.dev/~cprov
 
 The installation details are presented right below the 'Technical
@@ -295,7 +295,7 @@
     >>> tech_details = first_tag_by_class(
     ...     str(install_portlet), 'widget-body')
 
-    >>> print extract_text(tech_details)
+    >>> print(extract_text(tech_details))
     This PPA can be added to your system manually by copying
     the lines below and adding them to your system's software
     sources.
@@ -332,7 +332,7 @@
     ...     package_table = find_tag_by_id(
     ...         anon_browser.contents, 'packages_list')
     ...     for ppa_row in package_table.findChildren('tr'):
-    ...         print extract_text(ppa_row)
+    ...         print(extract_text(ppa_row))
 
     >>> print_archive_package_rows(anon_browser.contents)
     Package             Version         Uploaded by
@@ -367,7 +367,7 @@
 
 The link itself will point to the newer version in the distribution.
 
-    >>> print anon_browser.getLink('Newer version').url
+    >>> print(anon_browser.getLink('Newer version').url)
     http://launchpad.dev/ubuntu/+source/iceweasel/1.1
 
 A Latest updates portlet is included on the index page indicating the
@@ -375,7 +375,7 @@
 
     >>> latest_updates = find_portlet(
     ...     anon_browser.contents, "Latest updates")
-    >>> print extract_text(latest_updates)
+    >>> print(extract_text(latest_updates))
     Latest updates
     cdrkit ... ago
     Failed to build: i386
@@ -388,7 +388,7 @@
 
     >>> stats = find_portlet(
     ...     anon_browser.contents, "PPA statistics")
-    >>> print extract_text(stats)
+    >>> print(extract_text(stats))
     PPA statistics
     Activity
     1 update added during the past month.
@@ -410,7 +410,7 @@
     >>> anon_browser.reload()
     >>> stats = find_portlet(
     ...     anon_browser.contents, "PPA statistics")
-    >>> print extract_text(stats)
+    >>> print(extract_text(stats))
     PPA statistics
     Activity
     1 update added during the past month.
@@ -419,7 +419,7 @@
 Current build activity is linked to the builds page with the relevant
 filter.
 
-    >>> print anon_browser.getLink('1 package building').url
+    >>> print(anon_browser.getLink('1 package building').url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+builds?build_state=building
 
 
@@ -458,7 +458,7 @@
 link to a repository that doesn't exist yet.
 
     >>> anon_browser.open("http://launchpad.dev/~no-priv/+archive/ubuntu/ppa";)
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     PPA for No Privileges Person
     PPA description
     I am not allowed to say, I have no privs.
@@ -470,14 +470,14 @@
 
 It also contains a link to the 'PPA help page'.
 
-    >>> print anon_browser.getLink('PPA help page').url
+    >>> print(anon_browser.getLink('PPA help page').url)
     https://help.launchpad.net/Packaging/PPA
 
 The "sources list" widget isn't presented for empty PPAs either.
 
     >>> sources_list = find_tag_by_id(
     ...     anon_browser.contents, 'sources-list-entries')
-    >>> print sources_list
+    >>> print(sources_list)
     None
 
 Users will only be able to see it for PPAs that have, at least, one
@@ -490,7 +490,7 @@
     >>> anon_browser.reload()
     >>> sources_list = find_tag_by_id(
     ...     anon_browser.contents, 'sources-list-entries')
-    >>> print extract_text(sources_list)
+    >>> print(extract_text(sources_list))
     deb http://ppa.launchpad.dev/no-priv/ppa/ubuntu
         warty main
     deb-src http://ppa.launchpad.dev/no-priv/ppa/ubuntu
@@ -498,8 +498,8 @@
 
 Also the repository URL, within the sources.list snippet, is an actual link.
 
-    >>> print anon_browser.getLink(
-    ...     "http://ppa.launchpad.dev/no-priv/ppa/ubuntu";).url
+    >>> print(anon_browser.getLink(
+    ...     "http://ppa.launchpad.dev/no-priv/ppa/ubuntu";).url)
     http://ppa.launchpad.dev/no-priv/ppa/ubuntu
 
 
@@ -521,20 +521,20 @@
 
 It also has a link pointing to its corresponding help page.
 
-    >>> print no_priv_browser.getLink('Read about uploading').url
+    >>> print(no_priv_browser.getLink('Read about uploading').url)
     https://help.launchpad.net/Packaging/PPA/Uploading
 
 Anonymous access or users with no upload permission cannot see the
 upload hint section.
 
     >>> anon_browser.open("http://launchpad.dev/~no-priv/+archive/ubuntu/ppa";)
-    >>> print find_tag_by_id(
-    ...     anon_browser.contents, 'upload-hint')
+    >>> print(find_tag_by_id(
+    ...     anon_browser.contents, 'upload-hint'))
     None
 
     >>> admin_browser.open("http://launchpad.dev/~no-priv/+archive/ubuntu/ppa";)
-    >>> print find_tag_by_id(
-    ...     anon_browser.contents, 'upload-hint')
+    >>> print(find_tag_by_id(
+    ...     anon_browser.contents, 'upload-hint'))
     None
 
 
@@ -544,8 +544,8 @@
 the PPA creation. While the signing key isn't available nothing is
 presented to the users.
 
-    >>> print find_tag_by_id(
-    ...     anon_browser.contents, 'signing-key')
+    >>> print(find_tag_by_id(
+    ...     anon_browser.contents, 'signing-key'))
     None
 
 We will set a signing key for 'No Privileges' PPA as if it got
@@ -572,27 +572,27 @@
     >>> signing_key_section = find_tag_by_id(
     ...     anon_browser.contents, 'signing-key')
 
-    >>> print extract_text(signing_key_section)
+    >>> print(extract_text(signing_key_section))
     Signing key: 1024D/ABCDEF0123456789ABCDDCBA0000111112345678 (What is this?)
     Fingerprint: ABCDEF0123456789ABCDDCBA0000111112345678
 
 The key fingerprint links to the actual key available in the ubuntu
 keyserver.
 
-    >>> print anon_browser.getLink(
-    ...     '1024D/ABCDEF0123456789ABCDDCBA0000111112345678').url
+    >>> print(anon_browser.getLink(
+    ...     '1024D/ABCDEF0123456789ABCDDCBA0000111112345678').url)
     https://keyserver.ubuntu.com/pks/lookup?fingerprint=on&op=index&search=0xABCDEF0123456789ABCDDCBA0000111112345678
 
 Using software from a PPA can be hard for novices. We offer two
 links to the same help pop-up that describes how to add a PPA and
 its key to Ubuntu.
 
-    >>> print anon_browser.getLink('Read about installing').url
+    >>> print(anon_browser.getLink('Read about installing').url)
     http://launchpad.dev/+help-soyuz/ppa-sources-list.html
 
 And further down, next to the key id, we link to that same pop-up help:
 
-    >>> print anon_browser.getLink('What is this?').url
+    >>> print(anon_browser.getLink('What is this?').url)
     http://launchpad.dev/+help-soyuz/ppa-sources-list.html
 
 
@@ -602,13 +602,13 @@
 properly.
 
     >>> anon_browser.open("http://launchpad.dev/~mark/+archive/ubuntu/ppa";)
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     PPA for Mark Shuttleworth : Mark Shuttleworth
 
 Mark has sources only published in one archive, so he has no
 series-widget-div control to update them:
 
-    >>> print find_tag_by_id(anon_browser.contents, 'series-widget-div')
+    >>> print(find_tag_by_id(anon_browser.contents, 'series-widget-div'))
     None
 
 And the sources.list entries point to the right distribution release:
@@ -616,7 +616,7 @@
     >>> results = find_tag_by_id(
     ...     anon_browser.contents, 'sources-list-entries')
     >>> text = extract_text(results)
-    >>> print text
+    >>> print(text)
     deb http://ppa.launchpad.dev/mark/ppa/ubuntu breezy-autotest main
     deb-src http://ppa.launchpad.dev/mark/ppa/ubuntu breezy-autotest main
 
@@ -644,8 +644,8 @@
 the 'Most active' section.
 
     >>> anon_browser.open("http://launchpad.dev/ubuntu/+ppas";)
-    >>> print extract_text(
-    ...    find_tag_by_id(anon_browser.contents, 'ppa_most_active'))
+    >>> print(extract_text(
+    ...    find_tag_by_id(anon_browser.contents, 'ppa_most_active')))
     Most active
     PPAs with the highest number of uploads in the last 7 days.
     PPA for Celso Providelo       4 uploads
@@ -656,7 +656,7 @@
 PPA itself.
 
     >>> anon_browser.getLink('PPA for Celso Providelo').click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     PPA for Celso Providelo : Celso Providelo
 
 

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-build-record.txt'
--- lib/lp/soyuz/stories/soyuz/xx-build-record.txt	2015-10-06 06:48:01 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-build-record.txt	2018-07-01 09:01:37 +0000
@@ -47,7 +47,7 @@
     >>> anon_browser.open(build_url)
 
     >>> from lp.services.helpers import backslashreplace
-    >>> print backslashreplace(anon_browser.title)
+    >>> print(backslashreplace(anon_browser.title))
     i386 build : 1.0 : testing package : ubuntutest
 
 In the page body readers can see 2 sections, 'Build status' and 'Build
@@ -61,7 +61,7 @@
 to the build context, source package, archive, series, pocket and
 component.
 
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     i386 build of testing 1.0 in ubuntutest breezy-autotest RELEASE
     ...
     Build status
@@ -84,7 +84,7 @@
     >>> build.buildqueue_record.suspend()
     >>> logout()
     >>> anon_browser.open(build_url)
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     i386 build of testing 1.0 in ubuntutest breezy-autotest RELEASE
     ...
     Build status
@@ -108,23 +108,23 @@
 The 'Build details' section exists for all status and contains links
 to all the relevant entities involved in this build.
 
-    >>> print anon_browser.getLink('testing - 1.0').url
+    >>> print(anon_browser.getLink('testing - 1.0').url)
     http://launchpad.dev/ubuntutest/+source/testing/1.0
 
-    >>> print anon_browser.getLink('Primary Archive for Ubuntu Test').url
+    >>> print(anon_browser.getLink('Primary Archive for Ubuntu Test').url)
     http://launchpad.dev/ubuntutest
 
-    >>> print anon_browser.getLink('Breezy Badger Autotest').url
+    >>> print(anon_browser.getLink('Breezy Badger Autotest').url)
     http://launchpad.dev/ubuntutest/breezy-autotest
 
-    >>> print anon_browser.getLink('i386').url
+    >>> print(anon_browser.getLink('i386').url)
     http://launchpad.dev/ubuntutest/breezy-autotest/i386
 
 Pending build records can be 'rescored', which will directly affect
 the time they will get started. A link to the corresponding help text
 about 'Build scores' is available.
 
-    >>> print anon_browser.getLink("What's this").url
+    >>> print(anon_browser.getLink("What's this").url)
     https://help.launchpad.net/Packaging/BuildScores
 
 Adminstrators can rescore pending builds in a separate form.
@@ -140,7 +140,7 @@
     >>> print_feedback_messages(admin_browser.contents)
     Build rescored to 0.
 
-    >>> print extract_text(find_tag_by_id(admin_browser.contents, 'status'))
+    >>> print(extract_text(find_tag_by_id(admin_browser.contents, 'status')))
     Build status
     Needs building
     Cancel build
@@ -168,18 +168,18 @@
 
     >>> anon_browser.reload()
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'status'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'status')))
     Build status
     Currently building on Bob The Builder
     Build score:0 (What's this?)
     Started ... ago
 
-    >>> print anon_browser.getLink('Bob The Builder').url
+    >>> print(anon_browser.getLink('Bob The Builder').url)
     http://launchpad.dev/builders/bob
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'buildlog'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'buildlog')))
     Buildlog
     one line
     another line
@@ -190,8 +190,8 @@
 outdated information.
 
     >>> user_browser.open(anon_browser.url)
-    >>> print extract_text(
-    ...     find_tag_by_id(user_browser.contents, 'buildlog'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(user_browser.contents, 'buildlog')))
     Buildlog
     one line
     another line
@@ -214,8 +214,8 @@
 
     >>> anon_browser.reload()
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'status'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'status')))
     Build status
     Failed to upload on Bob The Builder
     Started ... ago
@@ -223,7 +223,7 @@
     buildlog (7 bytes)
     uploadlog (7 bytes)
 
-    >>> print anon_browser.getLink('Bob The Builder').url
+    >>> print(anon_browser.getLink('Bob The Builder').url)
     http://launchpad.dev/builders/bob
 
     >>> login(ANONYMOUS)
@@ -241,8 +241,8 @@
 'Build Status' section.
 
     >>> admin_browser.open(admin_browser.url)
-    >>> print extract_text(
-    ...     find_tag_by_id(admin_browser.contents, 'status'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(admin_browser.contents, 'status')))
     Build status
     Failed to upload on Bob The Builder Retry this build
     Started ... ago
@@ -250,14 +250,14 @@
     buildlog (7 bytes)
     uploadlog (7 bytes)
 
-    >>> print admin_browser.getLink('Retry this build').url
+    >>> print(admin_browser.getLink('Retry this build').url)
     http://launchpad.dev/ubuntutest/+source/testing/1.0/+build/.../+retry
 
 By clicking on the 'Retry this build' link, administrators are informed of
 the consequences of this action.
 
     >>> admin_browser.getLink('Retry this build').click()
-    >>> print extract_text(find_main_content(admin_browser.contents))
+    >>> print(extract_text(find_main_content(admin_browser.contents)))
     Retry i386 build of testing 1.0 in ubuntutest breezy-autotest RELEASE
     ...
     The status of i386 build of testing 1.0 in ubuntutest
@@ -272,8 +272,8 @@
 is changed.
 
     >>> admin_browser.getLink("Cancel").click()
-    >>> print extract_text(
-    ...     find_tag_by_id(admin_browser.contents, 'status'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(admin_browser.contents, 'status')))
     Build status
     Failed to upload on Bob The Builder Retry this build
     Started ... ago
@@ -290,7 +290,7 @@
     >>> print_feedback_messages(admin_browser.contents)
     Build has been queued
 
-    >>> print extract_text(find_tag_by_id(admin_browser.contents, 'status'))
+    >>> print(extract_text(find_tag_by_id(admin_browser.contents, 'status')))
     Build status
     Needs building
     Cancel build
@@ -324,8 +324,8 @@
 
     >>> anon_browser.reload()
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'status'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'status')))
     Build status
     Successfully built on Bob The Builder
     Started on 2008-01-01
@@ -333,17 +333,17 @@
     buildlog (7 bytes)
     testing_1.0_all.changes (15 bytes)
 
-    >>> print anon_browser.getLink('testing_1.0_all.changes').url
+    >>> print(anon_browser.getLink('testing_1.0_all.changes').url)
     http://.../+build/.../+files/testing_1.0_all.changes
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'binaries'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'binaries')))
     Binary packages
     Binary packages awaiting approval in NEW queue:
     testing-bin-1.0
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'files'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'files')))
     Built files
     Files resulting from this build:
     testing-bin_1.0_all.deb (8 bytes)
@@ -352,7 +352,7 @@
 linkified. That's because its `DistroArchSeriesBinaryPackageRelease`
 page does not exist yet.
 
-    >>> print anon_browser.getLink('testing-bin-1.0')
+    >>> print(anon_browser.getLink('testing-bin-1.0'))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
@@ -360,7 +360,7 @@
 On the other hand, users interested in testing the resulting binaries
 already have access to them.
 
-    >>> print anon_browser.getLink('testing-bin_1.0_all.deb').url
+    >>> print(anon_browser.getLink('testing-bin_1.0_all.deb').url)
     http://.../+build/.../+files/testing-bin_1.0_all.deb
 
 Again, note that the files are `ProxiedLibrarianFile` objects as well.
@@ -374,18 +374,18 @@
 
     >>> anon_browser.open(anon_browser.url)
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'binaries'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'binaries')))
     Binary packages
     Binary packages awaiting approval in UNAPPROVED queue:
     testing-bin-1.0
 
-    >>> print anon_browser.getLink('testing-bin-1.0')
+    >>> print(anon_browser.getLink('testing-bin-1.0'))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
-    >>> print anon_browser.getLink('testing-bin_1.0_all.deb').url
+    >>> print(anon_browser.getLink('testing-bin_1.0_all.deb').url)
     http://.../+build/.../+files/testing-bin_1.0_all.deb
 
 When new binaries are accepted by an archive administrator (See
@@ -398,18 +398,18 @@
 
     >>> anon_browser.open(anon_browser.url)
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'binaries'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'binaries')))
     Binary packages
     Binary packages awaiting publication:
     testing-bin-1.0
 
-    >>> print anon_browser.getLink('testing-bin-1.0')
+    >>> print(anon_browser.getLink('testing-bin-1.0'))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
-    >>> print anon_browser.getLink('testing-bin_1.0_all.deb').url
+    >>> print(anon_browser.getLink('testing-bin_1.0_all.deb').url)
     http://.../+build/.../+files/testing-bin_1.0_all.deb
 
 Once the accepted binary upload is processed by the backend, the
@@ -422,13 +422,13 @@
 
     >>> anon_browser.open(anon_browser.url)
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'binaries'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'binaries')))
     Binary packages
     Binary packages produced by this build:
     testing-bin 1.0
 
-    >>> print anon_browser.getLink('testing-bin 1.0').url
+    >>> print(anon_browser.getLink('testing-bin 1.0').url)
     http://launchpad.dev/ubuntutest/breezy-autotest/i386/testing-bin/1.0
 
 
@@ -457,12 +457,12 @@
 
     >>> anon_browser.open(ppa_build_url)
 
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     i386 build of ppa-test 1.0 : PPA for Celso Providelo : Celso Providelo
 
 The 'Build status' section is identical for PPAs.
 
-    >>> print extract_text(find_tag_by_id(anon_browser.contents, 'status'))
+    >>> print(extract_text(find_tag_by_id(anon_browser.contents, 'status')))
     Build status
     Successfully built on Bob The Builder
     Build score:...
@@ -471,19 +471,19 @@
     buildlog (6 bytes)
     ppa-test-bin_1.0_i386.changes (23 bytes)
 
-    >>> print anon_browser.getLink('Bob The Builder').url
+    >>> print(anon_browser.getLink('Bob The Builder').url)
     http://launchpad.dev/builders/bob
 
-    >>> print anon_browser.getLink('buildlog').url
+    >>> print(anon_browser.getLink('buildlog').url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+build/.../+files/buildlog_...
 
-    >>> print anon_browser.getLink('ppa-test-bin_1.0_i386.changes').url
+    >>> print(anon_browser.getLink('ppa-test-bin_1.0_i386.changes').url)
     http://.../+build/.../+files/ppa-test-bin_1.0_i386.changes
 
 'Build details', as mentioned above, doesn't link to the PPA source
 packages, since they do not exist.
 
-    >>> print extract_text(find_tag_by_id(anon_browser.contents, 'details'))
+    >>> print(extract_text(find_tag_by_id(anon_browser.contents, 'details')))
     Build details
     Source: ppa-test - 1.0
     Archive: PPA for Celso Providelo
@@ -492,29 +492,29 @@
     Pocket: Release
     Component: main
 
-    >>> print anon_browser.getLink('ppa-test - 1.0').url
+    >>> print(anon_browser.getLink('ppa-test - 1.0').url)
     Traceback (most recent call last):
     ...
     LinkNotFoundError
 
-    >>> print anon_browser.getLink('PPA for Celso Providelo').url
+    >>> print(anon_browser.getLink('PPA for Celso Providelo').url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa
 
-    >>> print anon_browser.getLink('Breezy Badger Autotest').url
+    >>> print(anon_browser.getLink('Breezy Badger Autotest').url)
     http://launchpad.dev/ubuntutest/breezy-autotest
 
-    >>> print anon_browser.getLink('i386', index=1).url
+    >>> print(anon_browser.getLink('i386', index=1).url)
     http://launchpad.dev/ubuntutest/breezy-autotest/i386
 
 Similarly, binary packages are not linkified in 'Binary packages'
 section for PPA builds.
 
-    >>> print extract_text(find_tag_by_id(anon_browser.contents, 'binaries'))
+    >>> print(extract_text(find_tag_by_id(anon_browser.contents, 'binaries')))
     Binary packages
     Binary packages produced by this build:
     ppa-test-bin-1.0
 
-    >>> print anon_browser.getLink('ppa-test-bin-1.0').url
+    >>> print(anon_browser.getLink('ppa-test-bin-1.0').url)
     Traceback (most recent call last):
     ...
     LinkNotFoundError
@@ -536,25 +536,25 @@
     ...  archive=cprov.archive, distroseries=distroseries))
     >>> logout()
     >>> anon_browser.open(ppa_build_url)
-    >>> print extract_text(find_tag_by_id(anon_browser.contents, 'details'))
+    >>> print(extract_text(find_tag_by_id(anon_browser.contents, 'details')))
     Build details
     ...
     Source package recipe build:
     ~cprov/product/mybranch recipe build in ubuntu shiny [~cprov/ubuntu/ppa]
     ...
 
-    >>> print anon_browser.getLink('~cprov/product/mybranch recipe build').url
+    >>> print(anon_browser.getLink('~cprov/product/mybranch recipe build').url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+recipebuild/...
 
 Finally, the 'Build files' section is identical for PPA builds.
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'files'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'files')))
     Built files
     Files resulting from this build:
     ppa-test-bin_1.0_all.deb (18 bytes)
 
-    >>> print anon_browser.getLink('ppa-test-bin_1.0_all.deb').url
+    >>> print(anon_browser.getLink('ppa-test-bin_1.0_all.deb').url)
     http://.../+build/.../+files/ppa-test-bin_1.0_all.deb
 
 
@@ -571,7 +571,7 @@
     >>> unused_binaries = stp.uploadBinaryForBuild(
     ...     imported_build, 'imported-bin')
 
-    >>> print imported_build.package_upload
+    >>> print(imported_build.package_upload)
     None
 
     >>> imported_build_url = (
@@ -581,14 +581,14 @@
 
     >>> anon_browser.open(imported_build_url)
 
-    >>> print backslashreplace(anon_browser.title)
+    >>> print(backslashreplace(anon_browser.title))
     i386 build : 666 : imported package : ubuntutest
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'binaries'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'binaries')))
     Binary packages
     Binary packages produced by this build:
     imported-bin 666
 
-    >>> print anon_browser.getLink('imported-bin 666').url
+    >>> print(anon_browser.getLink('imported-bin 666').url)
     http://launchpad.dev/ubuntutest/breezy-autotest/i386/imported-bin/666

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt	2018-07-01 09:01:37 +0000
@@ -19,7 +19,7 @@
     ...     by state and "mismatch" if the page, erroneously,  provide only
     ...     part of the required arguments to enable filter by name.
     ...     """
-    ...     assert '<input type="submit" value="Filter" />' in contents
+    ...     assert b'<input type="submit" value="Filter" />' in contents
     ...
     ...     field_state = find_tag_by_id(contents, 'build_state') is not None
     ...     field_name = find_tag_by_id(contents, 'build_text') is not None
@@ -41,30 +41,30 @@
 
     >>> anon_browser.open("http://launchpad.dev/ubuntu";)
     >>> anon_browser.getLink("Builds").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : Ubuntu
 
-    >>> print check_builds_options(anon_browser.contents)
+    >>> print(check_builds_options(anon_browser.contents))
     State and Name present
 
 For DistroSeries, an architecture filter is also presented:
 
     >>> anon_browser.open("http://launchpad.dev/ubuntu/hoary";)
     >>> anon_browser.getLink("Show builds").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : Hoary (5.04) : Ubuntu
 
-    >>> print check_builds_options(anon_browser.contents)
+    >>> print(check_builds_options(anon_browser.contents))
     State, Arch and Name present
 
 For DistroArchSeries, same as Distribution:
 
     >>> anon_browser.open("http://launchpad.dev/ubuntu/hoary/i386";)
     >>> anon_browser.getLink("Show builds").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : i386 : Hoary (5.04) : Ubuntu
 
-    >>> print check_builds_options(anon_browser.contents)
+    >>> print(check_builds_options(anon_browser.contents))
     State and Name present
 
 For Builder, same as Distribution:
@@ -72,10 +72,10 @@
     >>> anon_browser.mech_browser.set_handle_equiv(False)
     >>> anon_browser.open("http://launchpad.dev/builders/bob";)
     >>> anon_browser.getLink("View full history").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Build history : Bob The Builder : Build Farm
 
-    >>> print check_builds_options(anon_browser.contents)
+    >>> print(check_builds_options(anon_browser.contents))
     State and Name present
 
 For Archive (PPA), same as Distribution:
@@ -83,10 +83,10 @@
     >>> anon_browser.open(
     ...     "http://launchpad.dev/~cprov/+archive/+packages";)
     >>> anon_browser.getLink("View all builds").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : PPA for Celso Providelo : Celso Providelo
 
-    >>> print check_builds_options(anon_browser.contents)
+    >>> print(check_builds_options(anon_browser.contents))
     State and Name present
 
 For SourcePackage, it's only possible to filter by state.
@@ -94,17 +94,17 @@
     >>> anon_browser.open(
     ...   "http://launchpad.dev/ubuntu/hoary/+source/pmount";)
     >>> anon_browser.getLink("Show builds").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Builds : Hoary (5.04) : pmount package : Ubuntu
 
-    >>> print check_builds_options(anon_browser.contents)
+    >>> print(check_builds_options(anon_browser.contents))
     State present, Name not present
 
 The source package default state is "all states":
 
     >>> soup = find_main_content(anon_browser.contents)
     >>> [results] = soup.findAll(attrs={'selected': 'selected'})
-    >>> print extract_text(results)
+    >>> print(extract_text(results))
     All states
 
 The anonymous user checks the contents of the distro builds list page.
@@ -133,10 +133,10 @@
     ...     rule = 30 * '-'
     ...     build_rows = find_tags_by_class(contents, 'build-row')
     ...     for row in build_rows:
-    ...         print rule
-    ...         print row.td.img['title']
-    ...         print extract_text(row)
-    ...     print rule
+    ...         print(rule)
+    ...         print(row.td.img['title'])
+    ...         print(extract_text(row))
+    ...     print(rule)
 
     >>> print_build_rows(anon_browser.contents)
     ------------------------------
@@ -270,7 +270,7 @@
     >>> anon_browser.getControl(name="build_state").value = ['foo']
     Traceback (most recent call last):
     ...
-    ItemNotFoundError: insufficient items with name 'foo'
+    ItemNotFoundError: insufficient items with name u'foo'
 
 However even if anonymous user builds an URL with a incorrect value,
 code is prepared to raise the correct exception:
@@ -389,8 +389,8 @@
 
     >>> anon_browser.open("http://launchpad.dev/debian/+builds";)
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'no-default-result'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'no-default-result')))
     No 'Currently building' build records.
 
     >>> find_tag_by_id(anon_browser.contents, 'empty-result') is None
@@ -408,8 +408,8 @@
     ...     anon_browser.contents, 'no-default-result') is None
     True
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'empty-result'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'empty-result')))
     No matching builds.
 
 If there *are* builds present, the anonymous user does not see any
@@ -436,8 +436,8 @@
 When anonymous user first load only 'No packages currently building'
 message is presented.
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'no-default-result'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'no-default-result')))
     No build records.
 
     >>> find_tag_by_id(anon_browser.contents, 'empty-result') is None
@@ -452,8 +452,8 @@
     >>> find_tag_by_id(anon_browser.contents, 'no-default-result') is None
     True
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'empty-result'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'empty-result')))
     No matching builds.
 
 The described mechanism works similarly for:
@@ -473,8 +473,8 @@
     ...    "http://launchpad.dev/ubuntu//+builds?build_text=";
     ...    "commercialpackage&build_state=built")
 
-    >>> print extract_text(
-    ...    find_tags_by_class(anon_browser.contents, 'listing')[0])
+    >>> print(extract_text(
+    ...    find_tags_by_class(anon_browser.contents, 'listing')[0]))
     i386 build of commercialpackage 1.0-1 in ubuntu breezy-autotest RELEASE
     ...
 
@@ -484,8 +484,8 @@
     ...    "http://launchpad.dev/ubuntu/breezy-autotest/+builds";
     ...    "?build_text=commercialpackage&build_state=built")
 
-    >>> print extract_text(
-    ... find_tags_by_class(anon_browser.contents, 'listing')[0])
+    >>> print(extract_text(
+    ... find_tags_by_class(anon_browser.contents, 'listing')[0]))
     i386 build of commercialpackage 1.0-1 in ubuntu breezy-autotest RELEASE
     ...
 
@@ -520,7 +520,7 @@
 
     >>> anon_browser.open(
     ...     "http://launchpad.dev/ubuntu/+source/testing/1.0";)
-    >>> print extract_text(find_portlet(anon_browser.contents, 'Builds'))
+    >>> print(extract_text(find_portlet(anon_browser.contents, 'Builds')))
     Builds
     Warty: hppa i386
 
@@ -537,6 +537,6 @@
     >>> logout()
 
     >>> anon_browser.reload()
-    >>> print extract_text(find_portlet(anon_browser.contents, 'Builds'))
+    >>> print(extract_text(find_portlet(anon_browser.contents, 'Builds')))
     Builds
     Warty: hppa i386 (Unapproved)

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt	2015-10-01 10:25:19 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt	2018-07-01 09:01:37 +0000
@@ -51,14 +51,14 @@
 page, but it exists at:
 
     >>> anon_browser.open("http://launchpad.dev/ubuntu/+archives";)
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Ubuntu Copy Archives...
 
 This index of /ubuntu/+archives provides an overview describing
 what the viewer can expect to find here
 
     >>> main_content = find_main_content(anon_browser.contents)
-    >>> print extract_text(main_content)
+    >>> print(extract_text(main_content))
     Copy Archives related to Ubuntu
     'Copy' archives containing packages copied from other archives
     (the main archive or PPAs) for a distribution.
@@ -70,36 +70,36 @@
 details:
 
     >>> link = anon_browser.getLink("intrepid-security-rebuild")
-    >>> print link
+    >>> print(link)
     <Link text='intrepid-security-rebuild'
     url='http://launchpad.dev/ubuntu/+archive/intrepid-security-rebuild'>
 
     >>> link.click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Copy archive intrepid-security-rebuild for Joe Bloggs : Ubuntu
 
 Copy archives only have minor differences from PPAs in their presentation.
 In particular, the source.list entries are not displayed for a copy
 archive:
 
-    >>> print find_tag_by_id(anon_browser.contents, 'sources-list-entries')
+    >>> print(find_tag_by_id(anon_browser.contents, 'sources-list-entries'))
     None
 
 The package counters are not displayed for a copy archive:
 
-    >>> print find_tag_by_id(anon_browser.contents, 'package-counters')
+    >>> print(find_tag_by_id(anon_browser.contents, 'package-counters'))
     None
 
 Neither is the repository-size javascript present:
 
-    >>> print find_tag_by_id(anon_browser.contents, 'repository-size-update')
+    >>> print(find_tag_by_id(anon_browser.contents, 'repository-size-update'))
     None
 
 Whereas, for a PPA which uses the same template, the sources list is
 present:
 
     >>> anon_browser.open("http://launchpad.dev/~cprov/+archive";)
-    >>> print find_tag_by_id(anon_browser.contents, 'sources-list-entries')
+    >>> print(find_tag_by_id(anon_browser.contents, 'sources-list-entries'))
     <pre id="sources-list-entries"...
     ...
     deb-src ...</pre>
@@ -109,7 +109,7 @@
     >>> anon_browser.open("http://launchpad.dev/ubuntu/+archives";)
     >>> anon_browser.getLink("intrepid-security-rebuild").click()
     >>> anon_browser.getLink("View package details").click()
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     Packages in “Copy archive intrepid-security-rebuild...
 
 The list of copy archives for a distribution may also include private
@@ -173,15 +173,15 @@
     >>> nopriv_browser.open(
     ...     "http://launchpad.dev/ubuntu/+archive/disabled-security-rebuild";)
 
-    >>> print nopriv_browser.title
+    >>> print(nopriv_browser.title)
     Copy archive disabled-security-rebuild for No Privileges Person : Ubuntu
 
     >>> main_content = find_main_content(nopriv_browser.contents)
-    >>> print main_content.h1['class']
+    >>> print(main_content.h1['class'])
     disabled
 
     >>> tag = first_tag_by_class(nopriv_browser.contents, 'warning message')
-    >>> print extract_text(tag)
+    >>> print(extract_text(tag))
     This archive has been disabled.
 
 When a COPY archive gets enabled, No Privileges users can still access
@@ -193,14 +193,14 @@
 
     >>> nopriv_browser.reload()
 
-    >>> print nopriv_browser.title
+    >>> print(nopriv_browser.title)
     Copy archive disabled-security-rebuild for No Privileges Person : Ubuntu
 
     >>> main_content = find_main_content(nopriv_browser.contents)
-    >>> print main_content.h1['class']
+    >>> print(main_content.h1['class'])
     Traceback (most recent call last):
-    KeyError: 'class'
+    KeyError: u'class'
 
-    >>> print first_tag_by_class(nopriv_browser.contents, 'warning message')
+    >>> print(first_tag_by_class(nopriv_browser.contents, 'warning message'))
     None
 

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt	2018-05-04 21:59:32 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distributionsourcepackagerelease-pages.txt	2018-07-01 09:01:37 +0000
@@ -33,7 +33,7 @@
     ...     'http://launchpad.dev/ubuntutest/+source/testing-dspr')
     >>> anon_browser.getLink('1.0').click()
 
-    >>> print anon_browser.url
+    >>> print(anon_browser.url)
     http://launchpad.dev/ubuntutest/+source/testing-dspr/1.0
 
 Its title describes exactly what it is about.
@@ -69,7 +69,7 @@
 
 Bug references in the 'changelog' are 'linkified'.
 
-    >>> print anon_browser.getLink('1234').url
+    >>> print(anon_browser.getLink('1234').url)
     http://launchpad.dev/bugs/1234
 
 The email addresses mentioned in the 'changelog' are only rendered to
@@ -89,15 +89,15 @@
     ...
     LinkNotFoundError
 
-    >>> print user_browser.getLink('celso.providelo@xxxxxxxxxxxxx').url
+    >>> print(user_browser.getLink('celso.providelo@xxxxxxxxxxxxx').url)
     http://launchpad.dev/~cprov
 
 The following portlet presents its 'Upload details' and link to the
 users involved with it and the `DistroSeries` it was originally
 uploaded to.
 
-    >>> print extract_text(
-    ...     find_portlet(anon_browser.contents, 'Upload details'))
+    >>> print(extract_text(
+    ...     find_portlet(anon_browser.contents, 'Upload details')))
     Upload details
     Uploaded by:
     Foo Bar ... ago
@@ -112,13 +112,13 @@
     Urgency:
     Low Urgency
 
-    >>> print anon_browser.getLink('Foo Bar').url
+    >>> print(anon_browser.getLink('Foo Bar').url)
     http://launchpad.dev/~name16
 
-    >>> print anon_browser.getLink('Breezy Badger Autotest').url
+    >>> print(anon_browser.getLink('Breezy Badger Autotest').url)
     http://launchpad.dev/ubuntutest/breezy-autotest
 
-    >>> print anon_browser.getLink('Maintainer').url
+    >>> print(anon_browser.getLink('Maintainer').url)
     http://launchpad.dev/~maintainer
 
 If the upload was 'sponsored' (changes uploaded on behalf of someone
@@ -131,8 +131,8 @@
     >>> logout()
 
     >>> anon_browser.reload()
-    >>> print extract_text(
-    ...     find_portlet(anon_browser.contents, 'Upload details'))
+    >>> print(extract_text(
+    ...     find_portlet(anon_browser.contents, 'Upload details')))
     Upload details
     Uploaded by:
     Creator ... ago
@@ -140,10 +140,10 @@
     Foo Bar
     ...
 
-    >>> print anon_browser.getLink('Creator').url
+    >>> print(anon_browser.getLink('Creator').url)
     http://launchpad.dev/~creator
 
-    >>> print anon_browser.getLink('Foo Bar').url
+    >>> print(anon_browser.getLink('Foo Bar').url)
     http://launchpad.dev/~name16
 
 The 'Publishing' portlet lists all distroseries of the context
@@ -161,10 +161,10 @@
     >>> anon_browser.getLink('See full publishing history').click()
 
     >>> from lp.services.helpers import backslashreplace
-    >>> print backslashreplace(anon_browser.title)
+    >>> print(backslashreplace(anon_browser.title))
     Publishing history : 1.0 : testing-dspr package : ubuntutest
 
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     Publishing history of ...testing-dspr... 1.0 source package in ubuntutest
     1.0
     Publishing history
@@ -179,12 +179,12 @@
 The next section presented lists, and links, to all the builds of this
 source in the context distribution, grouped by distroseries.
 
-    >>> print extract_text(
-    ...     find_portlet(anon_browser.contents, 'Builds'))
+    >>> print(extract_text(
+    ...     find_portlet(anon_browser.contents, 'Builds')))
     Builds
     Breezy Badger Autotest: i386
 
-    >>> print anon_browser.getLink('i386').url
+    >>> print(anon_browser.getLink('i386').url)
     http://launchpad.dev/ubuntutest/+source/testing-dspr/1.0/+build/...
 
 The 'Downloads' section lists and links to the files for this
@@ -192,15 +192,15 @@
 distribution primary archive URL. It allows the files to be download
 via `dget`.
 
-    >>> print extract_text(
-    ...     find_portlet(anon_browser.contents, 'Downloads'))
+    >>> print(extract_text(
+    ...     find_portlet(anon_browser.contents, 'Downloads')))
     Downloads
     File                  Size      SHA-256 Checksum
     testing-dspr_1.0.dsc  28 bytes
     ac512102db9724bee18f26945efeeb82fdab89819e64e120fbfda755ca50c2c6
     View changes file
 
-    >>> print anon_browser.getLink('testing-dspr_1.0.dsc').url
+    >>> print(anon_browser.getLink('testing-dspr_1.0.dsc').url)
     http://.../ubuntutest/+archive/primary/+sourcefiles/testing-dspr/1.0/testing-dspr_1.0.dsc
 
 The 'Downloads' section also lists and link to package diffs when they
@@ -216,8 +216,8 @@
     >>> logout()
 
     >>> anon_browser.reload()
-    >>> print extract_text(
-    ...     find_portlet(anon_browser.contents, 'Downloads'))
+    >>> print(extract_text(
+    ...     find_portlet(anon_browser.contents, 'Downloads')))
     Downloads
     File                  Size      SHA-256 Checksum
     testing-dspr_1.0.dsc  28 bytes
@@ -226,22 +226,22 @@
     diff from 0.9 to 1.0 (7 bytes)
     View changes file
 
-    >>> print anon_browser.getLink('0.9 to 1.0').url
+    >>> print(anon_browser.getLink('0.9 to 1.0').url)
     http://.../.../testing-dspr_0.9_1.0.diff.gz
 
 Finally, the 'Binary packages' section lists all binary packages
 produced by this source. Each binary links to its specific
 `DistroSeriesBinaryPackage` page.
 
-    >>> print extract_text(
+    >>> print(extract_text(
     ...     find_portlet(anon_browser.contents,
-    ...     'Binary packages built by this source'))
+    ...     'Binary packages built by this source')))
     Binary packages built by this source
     foo-bin:
     No summary available for foo-bin in ubuntutest breezy-autotest.
     No description available for foo-bin in ubuntutest breezy-autotest.
 
-    >>> print anon_browser.getLink('foo-bin').url
+    >>> print(anon_browser.getLink('foo-bin').url)
     http://launchpad.dev/ubuntutest/breezy-autotest/+package/foo-bin
 
 The binary package summary and description are retrieved from the
@@ -265,9 +265,9 @@
     >>> logout()
 
     >>> anon_browser.reload()
-    >>> print extract_text(
+    >>> print(extract_text(
     ...     find_portlet(anon_browser.contents,
-    ...     'Binary packages built by this source'))
+    ...     'Binary packages built by this source')))
     Binary packages built by this source
     foo-bin: Foo app is great
     Well ... it does nothing, though
@@ -277,7 +277,7 @@
     >>> user_browser.open(
     ...     'http://launchpad.dev/ubuntutest/+source/testing-dspr')
     >>> user_browser.getLink('View full change log').click()
-    >>> print user_browser.url
+    >>> print(user_browser.url)
     http://launchpad.dev/ubuntutest/+source/testing-dspr/+changelog
 
     >>> print_tag_with_id(user_browser.contents, 'body_testing-dspr_1.0')
@@ -286,7 +286,7 @@
     LP: #1234
     celso.providelo@xxxxxxxxxxxxx
 
-    >>> print extract_text(find_main_content(user_browser.contents))
+    >>> print(extract_text(find_main_content(user_browser.contents)))
     Change log for ...testing-dspr... package in ubuntutest
     ...
     0.9

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroarchseries-binpackages.txt	2018-07-01 09:01:37 +0000
@@ -25,19 +25,19 @@
 this architecture:
 
     >>> table = find_tag_by_id(browser.contents, 'publishing-summary')
-    >>> print extract_text(table)
+    >>> print(extract_text(table))
     Date                  Status    Target     Pocket  Component Section  Priority  Phased updates  Version
     2006-04-11 13:00:01 UTC Published...Warty i386 release main     base Important                      1.0
     Published on 2006-04-11
     2005-06-18 00:00:00 UTC Published...Warty i386 release main     base     Extra                      0.9
     Published on 2005-06-18
-    >>> print table.findAll("tr")[2].td["colspan"]
+    >>> print(table.findAll("tr")[2].td["colspan"])
     10
 
 It also provides a link to the currently published version:
 
     >>> browser.getLink("0.9").click()
-    >>> print browser.title
+    >>> print(browser.title)
     0.9 : mozilla-firefox : i386 : Warty (4.10) : Ubuntu
 
 As well as a link to the related distribution source package:
@@ -45,7 +45,7 @@
     >>> browser.open(
     ...     'http://launchpad.dev/ubuntu/warty/i386/mozilla-firefox')
     >>> browser.getLink(id="source_package").click()
-    >>> print browser.title.decode('ascii', 'ignore')
+    >>> print(browser.title.decode('ascii', 'ignore'))
     iceweasel package : Ubuntu
 
 If the binary distribution does not have a current release, then the
@@ -53,7 +53,7 @@
 
     >>> browser.open(
     ...     'http://launchpad.dev/debian/woody/i386/pmount')
-    >>> print browser.getLink(id="source_package")
+    >>> print(browser.getLink(id="source_package"))
     Traceback (most recent call last):
     ...
     LinkNotFoundError
@@ -85,7 +85,7 @@
 Click on the wanted name to get on the DistroArchSeriesBinaryPackageRelease:
 
     >>> browser.getLink(url="mozilla-firefox/0.9").click()
-    >>> print browser.title
+    >>> print(browser.title)
     0.9 : mozilla-firefox : i386 : Warty (4.10) : Ubuntu
 
 This page represents an IDistroArchSeriesBinaryPackageRelease and is
@@ -93,24 +93,24 @@
 originated itself:
 
     >>> source_element = find_tag_by_id(browser.contents, 'source')
-    >>> print extract_text(source_element)
+    >>> print(extract_text(source_element))
     mozilla-firefox 0.9 source package in Ubuntu
 
-    >>> print source_element.find(name='a')['href']
+    >>> print(source_element.find(name='a')['href'])
     /ubuntu/+source/mozilla-firefox/0.9
 
 Also provide a section with the contained files, including the build,
 the respective librarian URL and size:
 
     >>> files_element = find_tag_by_id(browser.contents, 'files')
-    >>> print extract_text(files_element)
+    >>> print(extract_text(files_element))
     i386 build of mozilla-firefox 0.9 in ubuntu warty RELEASE
     produced these files:
     mozilla-firefox_0.9_i386.deb (3 bytes)
 
     >>> dfiles_element = find_tag_by_id(
     ...     browser.contents, 'downloadable-files')
-    >>> print dfiles_element.find(name='a')['href']
+    >>> print(dfiles_element.find(name='a')['href'])
     http://.../40/mozilla-firefox_0.9_i386.deb
 
 If the binary package did produce files, but those files have been
@@ -125,7 +125,7 @@
     >>> firefox_build = warty.getBuildRecords(
     ...     name=u'mozilla-firefox', arch_tag='i386')[0]
     >>> firefox_deb = firefox_build.binarypackages[0].files[0]
-    >>> print firefox_deb.libraryfile.filename
+    >>> print(firefox_deb.libraryfile.filename)
     mozilla-firefox_0.9_i386.deb
 
     Next we manually ensure that the file is deleted.
@@ -143,14 +143,14 @@
 
     >>> browser.reload()
     >>> files_element = find_tag_by_id(browser.contents, 'files')
-    >>> print extract_text(files_element)
+    >>> print(extract_text(files_element))
     i386 build of mozilla-firefox 0.9 in ubuntu warty RELEASE
     produced these files:
     mozilla-firefox_0.9_i386.deb (deleted)
 
     >>> dfiles_element = find_tag_by_id(
     ...     browser.contents, 'downloadable-files')
-    >>> print dfiles_element.find(name='a')
+    >>> print(dfiles_element.find(name='a'))
     None
 
 Binary Packages with no files to present results in a clear statement
@@ -158,7 +158,7 @@
 
     >>> browser.open(
     ...     'http://launchpad.dev/ubuntu/hoary/i386/pmount/0.1-1')
-    >>> print extract_text(find_tag_by_id(browser.contents, 'files'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'files')))
     i386 build of pmount 0.1-1 in ubuntu hoary RELEASE
     produced no files for this binary package.
 
@@ -180,8 +180,8 @@
 This page provides the publishing history of this BinaryPackage within
 this architecture:
 
-    >>> print extract_text(
-    ...     find_tag_by_id(browser.contents, 'publishing-summary'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(browser.contents, 'publishing-summary')))
     Date                  Status    Target     Pocket  Component Section Priority  Phased updates  Version
     2007-08-09 21:56:39 UTC Published...B...t i386 release partner devel Optional                  1.0-1
     Published on 2007-08-09
@@ -189,7 +189,7 @@
 It also provides a link to the currently published version:
 
     >>> browser.getLink("1.0-1").click()
-    >>> print browser.title
+    >>> print(browser.title)
     1.0-1 : commercialpackage : i386 : Breezy Badger Autotest (6.6.6) : Ubuntu
 
 A BinaryPackageRelease once published in a DistroArchSeries is
@@ -220,20 +220,20 @@
 Click on the wanted name to get on the DistroArchSeriesBinaryPackageRelease:
 
     >>> browser.getLink("commercialpackage").click()
-    >>> print browser.title
+    >>> print(browser.title)
     1.0-1 : commercialpackage : i386 : Breezy Badger Autotest (6.6.6) : Ubuntu
 
 This page represents an IDistroArchSeriesBinaryPackageRelease and is
 able to point the user to the IDistributionSourcePackageRelease which
 originated itself:
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'source'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'source')))
     commercialpackage 1.0-1 source package in Ubuntu
 
 Also provide a section with the contained files, including respective
 librarian URL and size:
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'files'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'files')))
     i386 build of commercialpackage 1.0-1 in ubuntu breezy-autotest RELEASE
     produced these files:
     commercialpackage_1.0-1_i386.deb (652 bytes)
@@ -253,8 +253,8 @@
     >>> anon_browser.open(
     ...     'http://launchpad.dev/ubuntu/warty/i386/pmount')
 
-    >>> print extract_text(
-    ...    find_tag_by_id(anon_browser.contents, 'publishing-summary'))
+    >>> print(extract_text(
+    ...    find_tag_by_id(anon_browser.contents, 'publishing-summary')))
     Date           Status      Target     Pocket  Component Section  Priority  Phased updates  Version
     2007-09-13 ... Superseded...Warty i386 release universe editors Important                    0.1-1
       Published on 2006-01-26

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt	2015-04-20 15:59:52 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroarchseries.txt	2018-07-01 09:01:37 +0000
@@ -15,13 +15,13 @@
     >>> anon_browser.getLink('4.10').click()
     >>> anon_browser.getLink('i386').click()
 
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     i386 : Warty (4.10) : Ubuntu
 
 We present only minimal information in this page, its main feature is
 to allow binary package searching.
 
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     Ubuntu Warty for i386
     ...
     Search binary packages
@@ -51,8 +51,8 @@
 package summary.
 
 More details are available by clicking on the binary package name.
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'search-results'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'search-results')))
     1 &rarr; 3 of 3 results
     First &bull; Previous &bull; Next &bull; Last
     mozilla-firefox: Mozilla Firefox Web Browser
@@ -66,8 +66,8 @@
 
     >>> anon_browser.getControl(name="text").value = "biscoito"
     >>> anon_browser.getControl('Search', index=0).click()
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'search-results'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'search-results')))
     No packages matching 'biscoito' are published in Ubuntu Warty i386.
 
 
@@ -76,16 +76,16 @@
 All users can browse to the builds page from the DistroArchSeries
 page. The builds page is described in 23-builds-page.txt.
 
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'global-actions'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'global-actions')))
     Show builds
 
 Only administrators can edit ('administer', in fact) the
 distroarchseries details.
 
     >>> admin_browser.open("http://launchpad.dev/ubuntu/warty/i386/";)
-    >>> print extract_text(
-    ...     find_tag_by_id(admin_browser.contents, 'global-actions'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(admin_browser.contents, 'global-actions')))
     Administer
     Show builds
 
@@ -109,7 +109,7 @@
 The page presents a cancellation link that returns the user back to the
 DistroArchSeries page if clicked:
 
-    >>> print admin_browser.getLink("Cancel").url
+    >>> print(admin_browser.getLink("Cancel").url)
     http://launchpad.dev/ubuntu/warty/i386
 
 Removing the official support for this DistroArchSeries.
@@ -120,7 +120,7 @@
 DistroArchSeries page.
 
     >>> admin_browser.getControl("Change").click()
-    >>> print admin_browser.url
+    >>> print(admin_browser.url)
     http://launchpad.dev/ubuntu/warty/i386
 
 There's also a notification message announcing the success of the change:
@@ -143,7 +143,7 @@
 
     >>> for warning in find_tags_by_class(
     ...     admin_browser.contents, 'exception'):
-    ...     print extract_text(warning)
+    ...     print(extract_text(warning))
     Changing the architecture tag will use large amounts of archive
     disk space, and may affect many people. Please be very careful.
 
@@ -153,8 +153,8 @@
 
     >>> def check_arch_list(distroseries='warty'):
     ...     anon_browser.open("http://launchpad.dev/ubuntu/%s"; % distroseries)
-    ...     print extract_text(find_tag_by_id(
-    ...         anon_browser.contents, 'portlet-architectures-list'))
+    ...     print(extract_text(find_tag_by_id(
+    ...         anon_browser.contents, 'portlet-architectures-list')))
 
     >>> check_arch_list()
     hppa (unofficial)
@@ -175,7 +175,7 @@
 
     >>> admin_browser.open("http://launchpad.dev/ubuntu/hoary";)
     >>> admin_browser.getLink("Add architecture").click()
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     Add a port of The Hoary Hedgehog...
 
 Ubuntu hoary already has i386 & hppa distroarchseries and should not
@@ -225,7 +225,7 @@
 When the new architecture has been created, it is displayed to the
 administrator.
 
-    >>> print admin_browser.title
+    >>> print(admin_browser.title)
     amd64 : Hoary (5.04) : Ubuntu
 
 And other users can see the just-created architecture listed in the

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt	2014-11-27 22:13:36 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroseries-binary-packages.txt	2018-07-01 09:01:37 +0000
@@ -5,11 +5,11 @@
 
     >>> browser.open(
     ...     'http://launchpad.dev/ubuntu/warty/+package/mozilla-firefox')
-    >>> print browser.title
+    >>> print(browser.title)
     mozilla-firefox : Warty (4.10) : Ubuntu
 
     >>> main_content = find_main_content(browser.contents)
-    >>> print extract_text(main_content)
+    >>> print(extract_text(main_content))
     Binary package “mozilla-firefox” in ubuntu warty
     Warty (4.10) mozilla-firefox
     Mozilla Firefox Web Browser
@@ -24,11 +24,11 @@
 And each publishing history item is a link to the relevant binary
 release:
 
-    >>> print browser.getLink('mozilla-firefox 0.9 in hppa (Release)').url
+    >>> print(browser.getLink('mozilla-firefox 0.9 in hppa (Release)').url)
     http://launchpad.dev/ubuntu/warty/hppa/mozilla-firefox/0.9
-    >>> print browser.getLink('mozilla-firefox 0.9 in i386 (Release)').url
+    >>> print(browser.getLink('mozilla-firefox 0.9 in i386 (Release)').url)
     http://launchpad.dev/ubuntu/warty/i386/mozilla-firefox/0.9
-    >>> print browser.getLink('mozilla-firefox 1.0 in i386 (Release)').url
+    >>> print(browser.getLink('mozilla-firefox 1.0 in i386 (Release)').url)
     http://launchpad.dev/ubuntu/warty/i386/mozilla-firefox/1.0
 
 The page also displays a link to the distro series source package
@@ -43,11 +43,11 @@
 
     >>> browser.open(
     ...     'http://launchpad.dev/ubuntu/hoary/+package/mozilla-firefox')
-    >>> print browser.title
+    >>> print(browser.title)
     mozilla-firefox : Hoary (5.04) : Ubuntu
 
     >>> main_content = find_main_content(browser.contents)
-    >>> print extract_text(main_content)
+    >>> print(extract_text(main_content))
     Binary package “mozilla-firefox” in ubuntu hoary
     Hoary (5.04) mozilla-firefox
     No summary available for mozilla-firefox in ubuntu hoary.

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt	2014-11-09 09:27:57 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroseries-index.txt	2018-07-01 09:01:37 +0000
@@ -9,8 +9,8 @@
     >>> anon_browser.getControl("Find a Package").click()
     >>> anon_browser.url
     'http://launchpad.dev/ubuntu/warty/+search?text=a'
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'search-results'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'search-results')))
     1 &rarr; 3 of 3 results
     First &bull; Previous &bull; Next &bull; Last
     foobar: foobar is bad

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt	2018-05-04 21:59:32 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt	2018-07-01 09:01:37 +0000
@@ -36,7 +36,7 @@
 Let's have a look at the firefox DistributionSourcePackage.
 
   >>> browser.getLink("mozilla-firefox").click()
-  >>> print browser.url
+  >>> print(browser.url)
   http://launchpad.dev/ubuntu/+source/mozilla-firefox
 
 Click the "See full publishing history" link to see specific information
@@ -44,12 +44,12 @@
 
   >>> browser.getLink("View full publishing history").click()
   >>> table = find_tag_by_id(browser.contents, 'publishing-summary')
-  >>> print extract_text(table)
+  >>> print(extract_text(table))
   Date                      Status     Target   Pocket   Component   Section   Version
   2006-02-13 12:19:00 UTC   Published  Warty    release  main        web       0.9
   Published on 2006-02-13
   2004-09-27 11:57:13 UTC   Pending    Warty   release   main        editors   0.9
-  >>> print table.findAll("tr")[2].td["colspan"]
+  >>> print(table.findAll("tr")[2].td["colspan"])
   8
 
 Jump back to the DistributionSourcePackage page to continue the tests:
@@ -66,7 +66,7 @@
 Any user can see the package summary.
 
     >>> content = browser.contents
-    >>> print extract_text(find_tag_by_id(content, 'summary'))
+    >>> print(extract_text(find_tag_by_id(content, 'summary')))
     mozilla-firefox: Mozilla Firefox Web Browser
     mozilla-firefox-data: No summary available for mozilla-firefox-data in
     ubuntu warty.
@@ -74,7 +74,7 @@
 This page provides its versions publications organised by pocket.
 We can see 'mozilla-firefox' is published once in pocket RELEASE:
 
-    >>> print extract_text(find_tag_by_id(content, 'publishing_history'))
+    >>> print(extract_text(find_tag_by_id(content, 'publishing_history')))
     Release
     The package versions that were published when the distribution release
     was made.
@@ -83,19 +83,19 @@
 The user can also download the files for the "currentrelease" (last
 published version) if they are available:
 
-  >>> print extract_text(find_portlet(
-  ...     content, 'Download files from current release (0.9)'))
+  >>> print(extract_text(find_portlet(
+  ...     content, 'Download files from current release (0.9)')))
   Download files from current release (0.9)
   File Size SHA-256 Checksum
   firefox_0.9.2.orig.tar.gz 9.5 MiB ...
 
-  >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url
+  >>> print(browser.getLink("firefox_0.9.2.orig.tar.gz").url)
   http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz
 
 This page also provides links to the binary packages generated by this
 source in a specfic architecture:
 
-  >>> print extract_text(find_tag_by_id(content, 'binaries'))
+  >>> print(extract_text(find_tag_by_id(content, 'binaries')))
   mozilla-firefox
   (hppa)
   (i386)
@@ -106,7 +106,7 @@
 Let's check the link to the binary package built on i386 architecture,
 a DistroArchSeriesBinaryPackage:
 
-  >>> print browser.getLink("i386").url
+  >>> print(browser.getLink("i386").url)
   http://launchpad.dev/ubuntu/warty/i386/mozilla-firefox
 
 More information about this page can be found at
@@ -213,7 +213,7 @@
   'http://launchpad.dev/ubuntu/warty/+source/mozilla-firefox/+changelog'
 
   >>> tag = find_tag_by_id(browser.contents, 'mozilla-firefox_0.9')
-  >>> print extract_text(tag)
+  >>> print(extract_text(tag))
   Mozilla dummy Changelog......
 
 
@@ -240,10 +240,10 @@
     >>> logout()
 
     >>> browser.getLink('copyright').click()
-    >>> print browser.title
+    >>> print(browser.title)
     Copyright : Warty (4.10) : mozilla-firefox package : Ubuntu
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'copyright'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'copyright')))
     Copyright 2010 Ford Prefect.
 
     >>> browser.open(
@@ -268,23 +268,23 @@
 There we can see the respective 'changelog' content for this version:
 
   >>> tag = find_tag_by_id(browser.contents, 'mozilla-firefox_0.9')
-  >>> print extract_text(tag)
+  >>> print(extract_text(tag))
   Mozilla dummy Changelog......
 
 With the possibility to download the entire changesfile (if available):
 
-  >>> print browser.getLink('View changes file').url
+  >>> print(browser.getLink('View changes file').url)
   http://.../52/mozilla-firefox_0.9_i386.changes
 
 And also download the files contained in this source, like '.orig',
 '.diff' and the DSC:
 
-  >>> print extract_text(find_portlet(browser.contents, 'Downloads'))
+  >>> print(extract_text(find_portlet(browser.contents, 'Downloads')))
   Downloads
   File Size SHA-256 Checksum
   firefox_0.9.2.orig.tar.gz 9.5 MiB ...
 
-  >>> print browser.getLink("firefox_0.9.2.orig.tar.gz").url
+  >>> print(browser.getLink("firefox_0.9.2.orig.tar.gz").url)
   http://launchpad.dev/ubuntu/+archive/primary/+sourcefiles/mozilla-firefox/0.9/firefox_0.9.2.orig.tar.gz
 
 If we go to the same page for alsa-utils, the changelog has text that is
@@ -305,7 +305,7 @@
 
   >>> user_browser.open(
   ...     "http://launchpad.dev/ubuntu/+source/commercialpackage/1.0-1";)
-  >>> print user_browser.getLink('foo.bar@xxxxxxxxxxxxx').url
+  >>> print(user_browser.getLink('foo.bar@xxxxxxxxxxxxx').url)
   http://launchpad.dev/~name16
 
 Let's check how the page behaves if we no files are present:
@@ -315,7 +315,7 @@
 
 The Downloads portlet indicates that no files are available.
 
-  >>> print extract_text(find_portlet(browser.contents, 'Downloads'))
+  >>> print(extract_text(find_portlet(browser.contents, 'Downloads')))
   Downloads
   No files available for download.
   No changes file available.
@@ -338,37 +338,37 @@
 This page provides its versions publications organised by pocket.
 We can see 'commercialpackage' is published once in pocket RELEASE:
 
-    >>> print browser.getLink('commercialpackage 1.0-1').url
+    >>> print(browser.getLink('commercialpackage 1.0-1').url)
     http://launchpad.dev/ubuntu/+source/commercialpackage/1.0-1
 
 The user can also download the files for the "currentrelease" (last
 published version) if they are available:
 
-    >>> print extract_text(find_portlet(
-    ...     browser.contents, 'Download files from current release (1.0-1)'))
+    >>> print(extract_text(find_portlet(
+    ...     browser.contents, 'Download files from current release (1.0-1)')))
     Download files from current release (1.0-1)
     File Size SHA-256 Checksum
     commercialpackage_1.0.orig.tar.gz 179 bytes ...
     commercialpackage_1.0-1.diff.gz 610 bytes ...
     commercialpackage_1.0-1.dsc 567 bytes ...
 
-    >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url
+    >>> print(browser.getLink("commercialpackage_1.0.orig.tar.gz").url)
     http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz
-    >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url
+    >>> print(browser.getLink("commercialpackage_1.0-1.diff.gz").url)
     http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz
-    >>> print browser.getLink("commercialpackage_1.0-1.dsc").url
+    >>> print(browser.getLink("commercialpackage_1.0-1.dsc").url)
     http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc
 
 This page also provides links to the binary packages generated by this
 source in a specfic architecture:
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'binaries'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'binaries')))
     commercialpackage (i386)
 
 Let's check the link to the binary package built on i386 architecture,
 a DistroArchSeriesBinaryPackage:
 
-    >>> print browser.getLink("i386").url
+    >>> print(browser.getLink("i386").url)
     http://launchpad.dev/ubuntu/breezy-autotest/i386/commercialpackage
 
 More information about this page can be found at
@@ -394,7 +394,7 @@
     'http://launchpad.dev/ubuntu/breezy-autotest/+source/commercialpackage/+changelog'
 
     >>> tag = find_tag_by_id(browser.contents, 'commercialpackage_1.0-1')
-    >>> print extract_text(tag)
+    >>> print(extract_text(tag))
     commercialpackage...
 
 
@@ -414,29 +414,29 @@
 There we can see the respective 'changelog' content for this version:
 
     >>> tag = find_tag_by_id(browser.contents, 'commercialpackage_1.0-1')
-    >>> print extract_text(tag)
+    >>> print(extract_text(tag))
     commercialpackage...
 
 With the possibility to download the entire changesfile (if available):
 
-    >>> print browser.getLink('View changes file').url
+    >>> print(browser.getLink('View changes file').url)
     http://.../65/commercialpackage_1.0-1_source.changes
 
 And also download the files contained in this source, like '.orig',
 '.diff' and the DSC:
 
-    >>> print extract_text(find_portlet(browser.contents, 'Downloads'))
+    >>> print(extract_text(find_portlet(browser.contents, 'Downloads')))
     Downloads
     File Size SHA-256 Checksum
     commercialpackage_1.0.orig.tar.gz 179 bytes ...
     commercialpackage_1.0-1.diff.gz 610 bytes ...
     commercialpackage_1.0-1.dsc 567 bytes ...
 
-    >>> print browser.getLink("commercialpackage_1.0.orig.tar.gz").url
+    >>> print(browser.getLink("commercialpackage_1.0.orig.tar.gz").url)
     http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0.orig.tar.gz
-    >>> print browser.getLink("commercialpackage_1.0-1.diff.gz").url
+    >>> print(browser.getLink("commercialpackage_1.0-1.diff.gz").url)
     http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.diff.gz
-    >>> print browser.getLink("commercialpackage_1.0-1.dsc").url
+    >>> print(browser.getLink("commercialpackage_1.0-1.dsc").url)
     http://launchpad.dev/ubuntu/+archive/partner/+sourcefiles/commercialpackage/1.0-1/commercialpackage_1.0-1.dsc
 
 
@@ -455,8 +455,8 @@
     ...     'http://launchpad.dev/ubuntu/+source/alsa-utils/'
     ...     '+publishinghistory')
 
-    >>> print extract_text(
-    ...    find_tag_by_id(anon_browser.contents, 'publishing-summary'))
+    >>> print(extract_text(
+    ...    find_tag_by_id(anon_browser.contents, 'publishing-summary')))
     Date           Status     Target    Pocket  Component Section Version
     2006-02-15 ... Pending    Warty     release main      editors 1.0.9a-4ubuntu1
       Copied from ubuntu hoary in Primary Archive for Ubuntu Linux

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-package-diff.txt'
--- lib/lp/soyuz/stories/soyuz/xx-package-diff.txt	2013-07-31 00:37:32 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-package-diff.txt	2018-07-01 09:01:37 +0000
@@ -118,8 +118,8 @@
     ...     'http://launchpad.dev/ubuntu/+source/biscuit/+changelog')
     >>> changes = find_tags_by_class(anon_browser.contents, 'boardComment')
     >>> for change in changes:
-    ...     print 30 * '='
-    ...     print extract_text(change)
+    ...     print(30 * '=')
+    ...     print(extract_text(change))
     ==============================
     1.0-3
     Pending in hoary-release
@@ -139,7 +139,7 @@
 
 Diffs already performed are rendered as link to the librarian file.
 
-    >>> print anon_browser.getLink('diff from 1.0-1 to 1.0-2').url
+    >>> print(anon_browser.getLink('diff from 1.0-1 to 1.0-2').url)
     http://.../biscuit_1.0-1_1.0-2.diff.gz
 
 On the other hand, diffs not yet performed are rendered as plain text,
@@ -160,7 +160,7 @@
     >>> logout()
 
     >>> anon_browser.reload()
-    >>> print anon_browser.getLink('diff from 1.0-2 to 1.0-3').url
+    >>> print(anon_browser.getLink('diff from 1.0-2 to 1.0-3').url)
     http://.../biscuit_1.0-2_1.0-3.diff.gz
 
 The same page section is presented in the corresponding
@@ -170,11 +170,11 @@
 
     >>> diff_section = find_tag_by_id(
     ...     anon_browser.contents, 'diff-for-1.0-3')
-    >>> print extract_text(diff_section)
+    >>> print(extract_text(diff_section))
     Available diffs
       diff from 1.0-2 to 1.0-3 (3 bytes)
 
-    >>> print anon_browser.getLink('diff from 1.0-2 to 1.0-3').url
+    >>> print(anon_browser.getLink('diff from 1.0-2 to 1.0-3').url)
     http://.../biscuit_1.0-2_1.0-3.diff.gz
 
 
@@ -198,7 +198,7 @@
     >>> expander_url = anon_browser.getLink(
     ...     id='pub%s-expander' % biscuit_ppa_id).url
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
     ...
     Available diffs
@@ -213,7 +213,7 @@
     >>> logout()
 
     >>> anon_browser.open(expander_url)
-    >>> print extract_text(anon_browser.contents)
+    >>> print(extract_text(anon_browser.contents))
     Publishing details
     ...
     Available diffs
@@ -222,6 +222,6 @@
 
 The text also links to the librarian file containing the diff.
 
-    >>> print anon_browser.getLink(
-    ...     'diff from 1.0-3 (in Ubuntu) to 1.0-4').url
+    >>> print(anon_browser.getLink(
+    ...     'diff from 1.0-3 (in Ubuntu) to 1.0-4').url)
     http://.../biscuit_1.0-3_1.0-4.diff.gz

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt'
--- lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt	2013-02-13 14:22:07 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-packagepublishinghistory.txt	2018-07-01 09:01:37 +0000
@@ -20,11 +20,11 @@
     ...     '+publishinghistory')
 
     >>> table = find_tag_by_id(anon_browser.contents, 'publishing-summary')
-    >>> print extract_text(table)
+    >>> print(extract_text(table))
     Date    Status    Target     Pocket   Component Section Version
     ... UTC Published Breezy ... release  main      base    666
     Published ... ago
-    >>> print table.findAll("tr")[2].td["colspan"]
+    >>> print(table.findAll("tr")[2].td["colspan"])
     8
 
 A publishing record will be shown as deleted in the publishing history after a
@@ -40,7 +40,7 @@
     ...     '+publishinghistory')
 
     >>> table = find_tag_by_id(anon_browser.contents, 'publishing-summary')
-    >>> print extract_text(table)
+    >>> print(extract_text(table))
     Date    Status    Target     Pocket   Component Section Version
             Deleted   Breezy ... release  main      base    666
     Deleted ... ago by ... fix bug 1
@@ -48,5 +48,5 @@
 
 Links to bug reports are added for bugs mentioned in the removal comment.
 
-    >>> print anon_browser.getLink("bug 1").url
+    >>> print(anon_browser.getLink("bug 1").url)
     http://launchpad.dev/bugs/1

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-person-packages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-person-packages.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-person-packages.txt	2018-07-01 09:01:37 +0000
@@ -6,7 +6,7 @@
 person's home page.
 
     >>> browser.open("http://launchpad.dev/~mark/+related-packages";)
-    >>> print browser.title
+    >>> print(browser.title)
     Related packages : Mark Shuttleworth
 
 This page is just a summary of the user's packages and will only
@@ -14,7 +14,7 @@
 has more links that take the user to batched listings in each category
 where all items can be perused.
 
-    >>> print extract_text(find_tag_by_id(browser.contents, 'navlinks'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'navlinks')))
     Related packages
     Maintained packages
     Uploaded packages
@@ -22,20 +22,20 @@
     Related projects
     Owned teams
 
-    >>> print browser.getLink("Maintained packages").url
+    >>> print(browser.getLink("Maintained packages").url)
     http://launchpad.dev/~mark/+maintained-packages
-    >>> print browser.getLink("Uploaded packages").url
+    >>> print(browser.getLink("Uploaded packages").url)
     http://launchpad.dev/~mark/+uploaded-packages
-    >>> print browser.getLink("PPA packages").url
+    >>> print(browser.getLink("PPA packages").url)
     http://launchpad.dev/~mark/+ppa-packages
-    >>> print browser.getLink("Related projects").url
+    >>> print(browser.getLink("Related projects").url)
     http://launchpad.dev/~mark/+related-projects
 
 Each category on the summary page has a heading that shows how many
 packages are being displayed.
 
     >>> browser.open("http://launchpad.dev/~mark/+related-packages";)
-    >>> print extract_text(find_tag_by_id(browser.contents, 'packages'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'packages')))
     Maintained packages
     Displaying first 5 packages out of 7 total
     ...
@@ -56,7 +56,7 @@
 
     >>> browser.open("http://launchpad.dev/~name16/+related-packages";)
     >>> link = browser.getLink("cnews")
-    >>> print link
+    >>> print(link)
     <Link text='cnews' url='http://launchpad.dev/ubuntu/+source/cnews'>
     >>> link.click()
     >>> browser.title
@@ -67,7 +67,7 @@
 
     >>> browser.open("http://launchpad.dev/~name16/+related-packages";)
     >>> link = browser.getLink(url="/ubuntu/hoary/+source/cnews")
-    >>> print link
+    >>> print(link)
     <Link text='Ubuntu Hoary' ...>
     >>> link.click()
     >>> browser.title
@@ -78,7 +78,7 @@
 
     >>> browser.open("http://launchpad.dev/~name16/+related-packages";)
     >>> link = browser.getLink(url="/ubuntu/+source/cnews/cr.g7-37")
-    >>> print link
+    >>> print(link)
     <Link ... url='http://launchpad.dev/ubuntu/+source/cnews/cr.g7-37'>
     >>> link.click()
     >>> browser.title
@@ -93,7 +93,7 @@
 
     >>> browser.open("http://launchpad.dev/~mark/+related-packages";)
     >>> browser.getLink("Maintained packages").click()
-    >>> print extract_text(find_tag_by_id(browser.contents, 'packages'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'packages')))
     1...5 of 7 results
     ...
     Name        Uploaded to  Version   When        Failures
@@ -109,8 +109,8 @@
     PPA for No Privileges Person
     >>> anon_browser.open(
     ...     "http://launchpad.dev/~no-priv/+maintained-packages";)
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'packages'))
+    >>> print(extract_text(
+    ...     find_tag_by_id(anon_browser.contents, 'packages')))
     Name...
     No Privileges Person does not maintain any packages.
 
@@ -118,7 +118,7 @@
 page that lists uploaded packages in batches.
 
     >>> browser.getLink("Uploaded packages").click()
-    >>> print extract_text(find_tag_by_id(browser.contents, 'packages'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'packages')))
     1...5 of 6 results
     ...
     Name    Uploaded to          Version When        Failures
@@ -129,7 +129,7 @@
 page that lists PPA packages in batches.
 
     >>> browser.getLink("PPA packages").click()
-    >>> print extract_text(find_tag_by_id(browser.contents, 'packages'))
+    >>> print(extract_text(find_tag_by_id(browser.contents, 'packages')))
     1...1 of 1 result
     ...
     Name      Uploaded to           Version  When        Failures
@@ -159,7 +159,7 @@
     >>> def print_ppa_rows(browser):
     ...     rows = find_tags_by_class(browser.contents, "ppa_row")
     ...     for row in rows:
-    ...         print extract_text(row)
+    ...         print(extract_text(row))
 
 Make a function to update the cached latest person source package release
 records.
@@ -394,10 +394,10 @@
     >>> def print_ppa_packages(contents):
     ...     packages = find_tags_by_class(contents, 'archive_package_row')
     ...     for pkg in packages:
-    ...         print extract_text(pkg)
+    ...         print(extract_text(pkg))
     ...     empty_section = find_tag_by_id(contents, 'empty-result')
     ...     if empty_section is not None:
-    ...         print extract_text(empty_section)
+    ...         print(extract_text(empty_section))
     >>> print_ppa_packages(admin_browser.contents)
     Source             Published   Status     Series   Section  Build Status
     source2 - 666...   a moment ago Deleted   ...
@@ -418,7 +418,7 @@
     ...     package_table = find_tag_by_id(
     ...         anon_browser.contents, 'packages_list')
     ...     for ppa_row in package_table.findChildren('tr'):
-    ...         print extract_text(ppa_row)
+    ...         print(extract_text(ppa_row))
 
     >>> anon_browser.open("http://launchpad.dev/~cprov/+archive/ppa";)
     >>> print_archive_package_rows(anon_browser)

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt'
--- lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt	2014-11-25 06:34:03 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-portlet-publishing-details.txt	2018-07-01 09:01:37 +0000
@@ -12,7 +12,7 @@
 These latter two come specifically from the publishing records, so take
 into account any overrides applied since the package was uploaded.
 
-    >>> print extract_text(browser.contents)
+    >>> print(extract_text(browser.contents))
     "alsa-utils" versions published in Ubuntu
     Warty (1.0.9a-4): main/base
     Hoary (1.0.9a-4ubuntu1): main/base
@@ -20,9 +20,9 @@
 
 Series and versions are linkified.
 
-    >>> print browser.getLink('Hoary').url
+    >>> print(browser.getLink('Hoary').url)
     http://bugs.launchpad.dev/ubuntu/hoary/+source/alsa-utils
-    >>> print browser.getLink('1.0.9a-4').url
+    >>> print(browser.getLink('1.0.9a-4').url)
     http://launchpad.dev/ubuntu/+source/alsa-utils/1.0.9a-4
 
 When a source package has never been published, the portlet will say so.
@@ -31,7 +31,7 @@
     ...     "http://launchpad.dev/ubuntu/+source/a52dec/";
     ...     "+portlet-publishing-details")
 
-    >>> print extract_text(browser.contents)
+    >>> print(extract_text(browser.contents))
     "a52dec" versions published in Ubuntu
     This source is not published in Ubuntu
 
@@ -43,6 +43,6 @@
     ...     "http://launchpad.dev/ubuntu/+source/cdrkit/";
     ...     "+portlet-publishing-details")
 
-    >>> print extract_text(browser.contents)
+    >>> print(extract_text(browser.contents))
     "cdrkit" versions published in Ubuntu
     Warty (1.0): main/editors

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-private-builds.txt'
--- lib/lp/soyuz/stories/soyuz/xx-private-builds.txt	2016-10-03 14:15:46 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-private-builds.txt	2018-07-01 09:01:37 +0000
@@ -62,7 +62,7 @@
 
     >>> anon_browser.mech_browser.set_handle_equiv(False)
     >>> anon_browser.open("http://launchpad.dev/+builds/frog";)
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     The frog builder...
     Current status
     Building private job
@@ -72,7 +72,7 @@
 
     >>> admin_browser.mech_browser.set_handle_equiv(False)
     >>> admin_browser.open("http://launchpad.dev/+builds/frog";)
-    >>> print extract_text(find_main_content(admin_browser.contents))
+    >>> print(extract_text(find_main_content(admin_browser.contents)))
     The frog builder...
     Current status
     Building
@@ -84,7 +84,7 @@
     >>> name12_browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
     >>> name12_browser.mech_browser.set_handle_equiv(False)
     >>> name12_browser.open("http://launchpad.dev/+builds/frog";)
-    >>> print extract_text(find_main_content(name12_browser.contents))
+    >>> print(extract_text(find_main_content(name12_browser.contents)))
     The frog builder...
     Current status
     Building private job
@@ -96,7 +96,7 @@
     ...     auth="Basic celso.providelo@xxxxxxxxxxxxx:test")
     >>> cprov_browser.mech_browser.set_handle_equiv(False)
     >>> cprov_browser.open("http://launchpad.dev/+builds/frog";)
-    >>> print extract_text(find_main_content(cprov_browser.contents))
+    >>> print(extract_text(find_main_content(cprov_browser.contents)))
     The frog builder...
     Current status
     Building
@@ -119,7 +119,7 @@
     >>> cprov_browser.open(
     ...     "http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/%s"; %
     ...     private_build_id)
-    >>> print cprov_browser.url
+    >>> print(cprov_browser.url)
     http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/...
 
 
@@ -139,7 +139,7 @@
     >>> anon_browser.open("http://launchpad.dev/+builds/bob/+history";)
     >>> [builds_list] = find_tags_by_class(
     ...     anon_browser.contents, 'builds_list')
-    >>> print extract_text(builds_list)
+    >>> print(extract_text(builds_list))
     Package:
     ...
     hppa build of mozilla-firefox 0.9 in ubuntu warty RELEASE
@@ -162,7 +162,7 @@
     >>> anon_browser.open("http://launchpad.dev/+builds/frog/+history";)
     >>> [builds_list] = find_tags_by_class(
     ...     anon_browser.contents, 'builds_list')
-    >>> print extract_text(builds_list)
+    >>> print(extract_text(builds_list))
     Package:
     ...
     No build records.
@@ -173,7 +173,7 @@
     >>> admin_browser.open("http://launchpad.dev/+builds/frog/+history";)
     >>> [builds_list] = find_tags_by_class(
     ...     admin_browser.contents, 'builds_list')
-    >>> print extract_text(builds_list)
+    >>> print(extract_text(builds_list))
     Package:
     ...
     i386 build of privacy-test 666 in ubuntutest breezy-autotest RELEASE
@@ -182,7 +182,7 @@
     >>> cprov_browser.open("http://launchpad.dev/+builds/frog/+history";)
     >>> [builds_list] = find_tags_by_class(
     ...     cprov_browser.contents, 'builds_list')
-    >>> print extract_text(builds_list)
+    >>> print(extract_text(builds_list))
     Package:
     ...
     i386 build of privacy-test 666 in ubuntutest breezy-autotest RELEASE
@@ -198,7 +198,7 @@
 An admin user can see builds even if they are private:
 
     >>> admin_browser.open("http://launchpad.dev/+builds";)
-    >>> print extract_text(find_main_content(admin_browser.contents))
+    >>> print(extract_text(find_main_content(admin_browser.contents)))
     The Launchpad build farm
     ...
     frog  Building i386 build of privacy-test ... [~cprov/ubuntu/p3a]
@@ -209,7 +209,7 @@
 Launchpad Buildd admins cannot see private builds.
 
     >>> name12_browser.open("http://launchpad.dev/+builds";)
-    >>> print extract_text(find_main_content(name12_browser.contents))
+    >>> print(extract_text(find_main_content(name12_browser.contents)))
     The Launchpad build farm
     ...
     frog  Building private job
@@ -220,7 +220,7 @@
 cprov can see his own private build:
 
     >>> cprov_browser.open("http://launchpad.dev/+builds";)
-    >>> print extract_text(find_main_content(cprov_browser.contents))
+    >>> print(extract_text(find_main_content(cprov_browser.contents)))
     The Launchpad build farm
     ...
     frog  Building i386 build of privacy-test ... [~cprov/ubuntu/p3a]
@@ -231,7 +231,7 @@
 Anonymous users cannot see the private build:
 
     >>> anon_browser.open("http://launchpad.dev/+builds";)
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     The Launchpad build farm
     2 available build machines, ...
     ...
@@ -255,9 +255,9 @@
     ...         "http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/%s"; %
     ...         private_build_id)
     ... except Unauthorized:
-    ...     print "Got expected exception"
+    ...     print("Got expected exception")
     ... else:
-    ...     print "Did not get expected exception"
+    ...     print("Did not get expected exception")
     Got expected exception
 
     >>> browser = setupBrowser(auth="Basic no-priv@xxxxxxxxxxxxx:test")
@@ -266,9 +266,9 @@
     ...         "http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/%s"; %
     ...         private_build_id)
     ... except Unauthorized:
-    ...     print "Got expected exception"
+    ...     print("Got expected exception")
     ... else:
-    ...     print "Did not get expected exception"
+    ...     print("Did not get expected exception")
     Got expected exception
 
 Let's make the iceweasel package available in breezy-autotest.
@@ -294,7 +294,7 @@
 Now the anonymous user can see the build:
 
     >>> anon_browser.open("http://launchpad.dev/+builds";)
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     The Launchpad build farm
     ...
     frog  Building i386 build of privacy-test 666 ... [~cprov/ubuntu/p3a]
@@ -306,7 +306,7 @@
 
     >>> browser.mech_browser.set_handle_equiv(False)
     >>> browser.open("http://launchpad.dev/+builds";)
-    >>> print extract_text(find_main_content(browser.contents))
+    >>> print(extract_text(find_main_content(browser.contents)))
     The Launchpad build farm
     ...
     frog  Building i386 build of privacy-test 666 ... [~cprov/ubuntu/p3a]
@@ -319,13 +319,13 @@
     >>> anon_browser.open(
     ...     "http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/%s"; %
     ...     private_build_id)
-    >>> print anon_browser.title
+    >>> print(anon_browser.title)
     i386 build of privacy-test 666 : PPA named p3a for Celso Providelo : Celso Providelo
 
     >>> browser.open(
     ...     "http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/%s"; %
     ...     private_build_id)
-    >>> print browser.title
+    >>> print(browser.title)
     i386 build of privacy-test 666 : PPA named p3a for Celso Providelo : Celso Providelo
 
 Similarly, when accessing the distribution source package release page,
@@ -334,9 +334,9 @@
     >>> browser.open(
     ...     "http://launchpad.dev/ubuntutest/+source/privacy-test/666";)
     >>> portlet = find_portlet(browser.contents, 'Builds')
-    >>> print extract_text(portlet)
+    >>> print(extract_text(portlet))
     Builds
     Breezy Badger Autotest: i386
 
-    >>> print browser.getLink('i386').url
+    >>> print(browser.getLink('i386').url)
     http://launchpad.dev/~cprov/+archive/ubuntu/p3a/+build/...

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages-motu.txt	2018-07-01 09:01:37 +0000
@@ -34,7 +34,7 @@
     >>> def print_queue(contents):
     ...     queue_rows = find_tags_by_class(contents, "queue-row")
     ...     for row in queue_rows:
-    ...         print extract_text(row)
+    ...         print(extract_text(row))
 
     >>> motu_browser.open(
     ...     "http://launchpad.dev/ubuntu/breezy-autotest/";)

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt'
--- lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2017-02-12 09:34:50 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-queue-pages.txt	2018-07-01 09:01:37 +0000
@@ -31,7 +31,7 @@
     ...     main_archive, name12)
     >>> from operator import attrgetter
     >>> for permission in sorted(permissions, key=attrgetter("id")):
-    ...     print permission.component.name
+    ...     print(permission.component.name)
     main
     restricted
     universe
@@ -64,7 +64,7 @@
     >>> def print_queue(contents):
     ...     queue_rows = find_tags_by_class(contents, "queue-row")
     ...     for row in queue_rows:
-    ...         print extract_text(row)
+    ...         print(extract_text(row))
 
     >>> print_queue(anon_browser.contents)
     Package             Version     Component Section Priority Sets Pocket  When
@@ -78,11 +78,11 @@
 The package name in the results list is a clickable link to the changes
 file for that upload.
 
-    >>> print anon_browser.getLink("netapplet-1.0.0.tar.gz")
+    >>> print(anon_browser.getLink("netapplet-1.0.0.tar.gz"))
     <Link text='netapplet-1.0.0.tar.gz'
 	  url='http://.../+upload/7/+files/netapplet-1.0.0.tar.gz'>
 
-    >>> print anon_browser.getLink("alsa-utils")
+    >>> print(anon_browser.getLink("alsa-utils"))
     <Link text='alsa-utils'
 	  url='http://.../+upload/4/+files/netapplet-1.0.0.tar.gz'>
 
@@ -209,7 +209,7 @@
     >>> anon_browser.getControl(name="queue_text").value = ''
     >>> anon_browser.getControl("Update").click()
 
-    >>> print find_tag_by_id(anon_browser.contents, 'queue-4-icon')
+    >>> print(find_tag_by_id(anon_browser.contents, 'queue-4-icon'))
     <span class="expander-link" id="queue-4-icon">&nbsp;</span>
 
 The 'filelist' is expanded as one or more table rows, right below the
@@ -222,7 +222,7 @@
 by its size, one file per line. Expired files have no size.
 
     >>> for row in filelist:
-    ...     print extract_text(row)
+    ...     print(extract_text(row))
     alsa-utils_1.0.9a-4ubuntu1.dsc (3 bytes)
     alsa-utils_1.0.9a-4ubuntu1.diff.gz
     diff from 1.0.9a-4 to 1.0.9a-4ubuntu1 (11 bytes)
@@ -231,7 +231,7 @@
 Expired files have no link, so we just get None.
 
     >>> for row in filelist:
-    ...     print row.find('a')
+    ...     print(row.find('a'))
     <a href="http://.../+upload/4/+files/alsa-utils_1.0.9a-4ubuntu1.dsc";>
       alsa-utils_1.0.9a-4ubuntu1.dsc
     </a>
@@ -245,7 +245,7 @@
 version, component, section and priority.
 
     >>> [filelist] = find_tags_by_class(anon_browser.contents, 'queue-2')
-    >>> print extract_text(filelist)
+    >>> print(extract_text(filelist))
     pmount_1.0-1_all.deb (18 bytes) NEW 0.1-1 main base important
 
 XXX cprov 20070726: we should extend the test when we are able to
@@ -364,11 +364,11 @@
     >>> run_package_upload_notification_jobs()
     >>> [changer_notification, signer_notification,
     ...  announcement] = pop_notifications()
-    >>> print changer_notification['To']
+    >>> print(changer_notification['To'])
     Daniel Silverstone <daniel.silverstone@xxxxxxxxxxxxx>
-    >>> print signer_notification['To']
+    >>> print(signer_notification['To'])
     Foo Bar <foo.bar@xxxxxxxxxxxxx>
-    >>> print announcement['To']
+    >>> print(announcement['To'])
     autotest_changes@xxxxxxxxxx
 
 Forcing a duplicated submission on a queue item is recognised.  Here we
@@ -509,7 +509,7 @@
 
     >>> filelist = find_tags_by_class(anon_browser.contents, 'queue-2')
     >>> for row in filelist:
-    ...     print extract_text(row)
+    ...     print(extract_text(row))
     pmount_1.0-1_all.deb (18 bytes) NEW 0.1-1 restricted admin extra
 
 'netapplet' has gone straight to the 'done' queue because it's a single
@@ -557,7 +557,7 @@
 and how that's stated in the notification email body.
 
     >>> body = rejection.get_payload()[0]
-    >>> print body.as_string() # doctest: -NORMALIZE_WHITESPACE
+    >>> print(body.as_string()) # doctest: -NORMALIZE_WHITESPACE
     Content-Type: text/plain; charset="utf-8"
     MIME-Version: 1.0
     Content-Transfer-Encoding: quoted-printable

=== modified file 'lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt'
--- lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt	2015-06-26 14:00:41 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-sourcepackage-changelog.txt	2018-07-01 09:01:37 +0000
@@ -16,8 +16,8 @@
     * Answers - http://answers.launchpad.dev/ubuntu/+source/pmount
     Main heading: Change logs for ...pmount... in Hoary
 
-    >>> print extract_text(
-    ...           find_tag_by_id(user_browser.contents, 'changelogs'))
+    >>> print(extract_text(
+    ...           find_tag_by_id(user_browser.contents, 'changelogs')))
     This is a placeholder changelog for pmount 0.1-2
     pmount (0.1-1) hoary; urgency=low
     * Fix description (Malone #1)
@@ -29,8 +29,8 @@
 
     >>> user_browser.open(
     ...     "http://launchpad.dev/ubuntu/hoary/+source/alsa-utils/+changelog";)
-    >>> print extract_text(
-    ...           find_tag_by_id(user_browser.contents, 'changelogs'))
+    >>> print(extract_text(
+    ...           find_tag_by_id(user_browser.contents, 'changelogs')))
     alsa-utils (1.0.9a-4ubuntu1) hoary; urgency=low
     * Placeholder
     LP: #10
@@ -71,7 +71,7 @@
 
     >>> anon_browser.open(
     ...     "http://launchpad.dev/ubuntu/hoary/+source/alsa-utils/+changelog";)
-    >>> print extract_text(find_main_content(anon_browser.contents))
+    >>> print(extract_text(find_main_content(anon_browser.contents)))
     Change logs for ...alsa-utils... in Hoary
     ...
     -- Sample Person &lt;email address hidden&gt; Tue, 7 Feb 2006 12:10:08 +0300
@@ -88,7 +88,7 @@
     ...           "commercialpackage/+changelog")
     >>> changelog = find_tag_by_id(
     ...           user_browser.contents, 'commercialpackage_1.0-1')
-    >>> print extract_text(changelog.find('a'))
+    >>> print(extract_text(changelog.find('a')))
     foo.bar@xxxxxxxxxxxxx
 
 Browsing the individual sourcepackage changelog.
@@ -99,8 +99,8 @@
 The changelog is still linkified here so that the bug links work,
 although the version link will point to the same page we're already on.
 
-    >>> print find_tag_by_id(
-    ...           user_browser.contents, 'alsa-utils_1.0.9a-4ubuntu1')
+    >>> print(find_tag_by_id(
+    ...           user_browser.contents, 'alsa-utils_1.0.9a-4ubuntu1'))
     <pre ... id="alsa-utils_1.0.9a-4ubuntu1">alsa-utils (1.0.9a-4ubuntu1) ...
     <BLANKLINE>
     ...

=== modified file 'lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt'
--- lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt	2018-07-01 09:01:37 +0000
@@ -45,7 +45,7 @@
     >>> response = agent_webservice.named_post(
     ...   joe['self_link'], 'getArchiveSubscriptionURL', {},
     ...   archive=cp3a['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
     This person does not have a valid subscription for the target archive
@@ -55,7 +55,7 @@
 
     >>> response = agent_webservice.named_post(cp3a['self_link'],
     ...   'newSubscription', subscriber=joe['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
     Location: http://api.launchpad.dev/beta/.../+subscriptions/joe
@@ -68,7 +68,7 @@
     >>> response = agent_webservice.named_post(
     ...   joe['self_link'], 'getArchiveSubscriptionURL', {},
     ...   archive=cp3a['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     "http://joe:...@private-ppa.launchpad.dev/.../commercial/ubuntu";
@@ -78,7 +78,7 @@
 
     >>> response = agent_webservice.named_get(
     ...   joe['self_link'], 'getArchiveSubscriptionURLs')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["http://joe:...@private-ppa.launchpad.dev/.../commercial/ubuntu";]
@@ -88,7 +88,7 @@
     >>> response = joe_webservice.named_post(
     ...   joe['self_link'], 'getArchiveSubscriptionURL', {},
     ...   archive=cp3a['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     "http://joe:...@private-ppa.launchpad.dev/.../commercial/ubuntu";
@@ -98,7 +98,7 @@
     >>> response = joe_webservice.named_post(
     ...   cprov['self_link'], 'getArchiveSubscriptionURL', {},
     ...   archive=cp3a['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
     Only the context user can call this method
@@ -186,7 +186,7 @@
     >>> subscription_link = subscriptions['entries'][0]['self_link']
     >>> response = commercial_webservice.named_post(
     ...     subscription_link, 'cancel', api_version='devel')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 

=== modified file 'lib/lp/soyuz/stories/webservice/xx-archive.txt'
--- lib/lp/soyuz/stories/webservice/xx-archive.txt	2016-03-03 18:41:27 +0000
+++ lib/lp/soyuz/stories/webservice/xx-archive.txt	2018-07-01 09:01:37 +0000
@@ -5,11 +5,11 @@
 distribution archives.
 
     >>> cprov_archive = webservice.get("/~cprov/+archive/ubuntu/ppa").jsonBody()
-    >>> print cprov_archive['self_link']
+    >>> print(cprov_archive['self_link'])
     http://.../~cprov/+archive/ubuntu/ppa
 
     >>> main = webservice.get("/ubuntu/+archive/primary").jsonBody()
-    >>> print main['self_link']
+    >>> print(main['self_link'])
     http://.../ubuntu/+archive/primary
 
 We publish a subset of their attributes.
@@ -76,7 +76,7 @@
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> foo_bar = getUtility(IPersonSet).getByName('name16')
     >>> [a_key] = foo_bar.gpg_keys
-    >>> print a_key.fingerprint
+    >>> print(a_key.fingerprint)
     ABCDEF0123456789ABCDDCBA0000111112345678
 
     >>> cprov = getUtility(IPersonSet).getByName('cprov')
@@ -84,7 +84,7 @@
     ...     a_key.fingerprint)
     >>> removeSecurityProxy(cprov.archive).signing_key_owner = (
     ...     a_key.owner)
-    >>> print cprov.archive.signing_key_fingerprint
+    >>> print(cprov.archive.signing_key_fingerprint)
     ABCDEF0123456789ABCDDCBA0000111112345678
 
     >>> logout()
@@ -92,7 +92,7 @@
 And then the new attribute value is exported as a string.
 
     >>> cprov_archive = webservice.get("/~cprov/+archive/ubuntu/ppa").jsonBody()
-    >>> print cprov_archive['signing_key_fingerprint']
+    >>> print(cprov_archive['signing_key_fingerprint'])
     ABCDEF0123456789ABCDDCBA0000111112345678
 
 Distributions can provide information about their archives.  Looking
@@ -100,7 +100,7 @@
 
     >>> distros = webservice.get("/distros").jsonBody()
     >>> for entry in distros['entries']:
-    ...    print entry['self_link']
+    ...    print(entry['self_link'])
     http://.../ubuntu
     http://.../kubuntu
     http://.../ubuntutest
@@ -112,7 +112,7 @@
 
 "ubuntutest" has a "main_archive" which is always present:
 
-    >>> print ubuntutest['main_archive_link']
+    >>> print(ubuntutest['main_archive_link'])
     http://.../ubuntutest/+archive/primary
 
 The archive has the following attributes:
@@ -141,7 +141,7 @@
 
 A distribution can also provide a list of all its archives:
 
-    >>> print ubuntutest['archives_collection_link']
+    >>> print(ubuntutest['archives_collection_link'])
     http://.../ubuntutest/archives
     >>> archives = webservice.get(
     ...     ubuntutest['archives_collection_link']).jsonBody()
@@ -153,7 +153,7 @@
 
     >>> bogus_archive = (
     ...     "http://api.launchpad.dev/beta/ubuntutest/+archive/bogus";)
-    >>> print webservice.get(bogus_archive)
+    >>> print(webservice.get(bogus_archive))
     HTTP/1.1 404 Not Found
     ...
     Object: ..., name: u'bogus'
@@ -253,12 +253,12 @@
     >>> def show_permission_entries(permissions):
     ...     for entry in sorted(permissions['entries'],
     ...                         key=permission_entry_sort_key):
-    ...         print entry['permission']
-    ...         print entry['person_link']
-    ...         print entry['component_name']
-    ...         print entry['source_package_name']
-    ...         print entry['pocket']
-    ...         print entry['distroseries_link']
+    ...         print(entry['permission'])
+    ...         print(entry['person_link'])
+    ...         print(entry['component_name'])
+    ...         print(entry['source_package_name'])
+    ...         print(entry['pocket'])
+    ...         print(entry['distroseries_link'])
 
 `getAllPermissions` returns all permissions on the archive.
 
@@ -312,9 +312,9 @@
 
 Passing a bad package name results in an error:
 
-    >>> print user_webservice.named_get(
+    >>> print(user_webservice.named_get(
     ...     ubuntu['main_archive_link'], 'getUploadersForPackage',
-    ...     source_package_name="badpackage")
+    ...     source_package_name="badpackage"))
     HTTP/1.1 404 Not Found
     ...
 
@@ -345,10 +345,10 @@
 
 And here's a packageset to play with later:
 
-    >>> print webservice.named_post(
+    >>> print(webservice.named_post(
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'umbrella', description=u'Contains all source packages',
-    ...     owner=name12['self_link'])
+    ...     owner=name12['self_link']))
     HTTP/1.1 201 Created
     ...
 
@@ -365,7 +365,7 @@
     ...     ubuntu['main_archive_link'], 'newPackageUploader', {},
     ...     person=name12['self_link'],
     ...     source_package_name='mozilla-firefox')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'newPackageUploader', 'launchpad.Edit')
@@ -374,7 +374,7 @@
     ...     ubuntu['main_archive_link'], 'newPackagesetUploader', {},
     ...     person=name12['self_link'],
     ...     packageset=packageset['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'newPackagesetUploader', 'launchpad.Edit')
@@ -383,7 +383,7 @@
     ...     ubuntu['main_archive_link'], 'newComponentUploader', {},
     ...     person=name12['self_link'],
     ...     component_name='restricted')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'newComponentUploader', 'launchpad.Edit')
@@ -399,13 +399,13 @@
     ...     ubuntu['main_archive_link'], 'newPackageUploader', {},
     ...     person=name12['self_link'],
     ...     source_package_name='mozilla-firefox')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
     >>> new_permission = user_webservice.get(
     ...     response.getHeader('Location')).jsonBody()
-    >>> print new_permission['self_link']
+    >>> print(new_permission['self_link'])
     http://.../ubuntu/+archive/primary/+upload/name12?type=packagename&item=mozilla-firefox
 
     >>> show_mozilla_permissions()
@@ -414,10 +414,10 @@
 
 deletePackageUploader() removes that permission:
 
-    >>> print ubuntu_owner_webservice.named_post(
+    >>> print(ubuntu_owner_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'deletePackageUploader', {},
     ...     person=name12['self_link'],
-    ...     source_package_name='mozilla-firefox')
+    ...     source_package_name='mozilla-firefox'))
     HTTP/1.1 200 Ok
     ...
 
@@ -440,9 +440,9 @@
 
 Passing a bad component name results in an error:
 
-    >>> print cjwatson_webservice.named_get(
+    >>> print(cjwatson_webservice.named_get(
     ...     ubuntu['main_archive_link'], 'getUploadersForComponent',
-    ...     component_name="badcomponent")
+    ...     component_name="badcomponent"))
     HTTP/1.1 404 Not Found
     ...
 
@@ -460,13 +460,13 @@
     ...     ubuntu['main_archive_link'], 'newComponentUploader', {},
     ...     person=name12['self_link'],
     ...     component_name='restricted')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
     >>> new_permission = user_webservice.get(
     ...     response.getHeader('Location')).jsonBody()
-    >>> print new_permission['self_link']
+    >>> print(new_permission['self_link'])
     http://.../ubuntu/+archive/primary/+upload/name12?type=component&item=restricted
 
     >>> show_component_permissions()
@@ -490,10 +490,10 @@
 
 deleteComponentUploader() removes that permission:
 
-    >>> print ubuntu_owner_webservice.named_post(
+    >>> print(ubuntu_owner_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'deleteComponentUploader', {},
     ...     person=name12['self_link'],
-    ...     component_name='restricted')
+    ...     component_name='restricted'))
     HTTP/1.1 200 Ok
     ...
 
@@ -523,25 +523,25 @@
 
     >>> no_priv = webservice.get("/~no-priv").jsonBody()
 
-    >>> print user_webservice.named_post(
+    >>> print(user_webservice.named_post(
     ...     cprov_archive['self_link'], 'newComponentUploader', {},
-    ...     person=no_priv['self_link'], component_name='main')
+    ...     person=no_priv['self_link'], component_name='main'))
     HTTP/1.1 401 Unauthorized
     ...
 
     >>> cprov_webservice = webservice_for_person(
     ...     cprov, permission=OAuthPermission.WRITE_PUBLIC)
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     cprov_archive['self_link'], 'newComponentUploader', {},
-    ...     person=no_priv['self_link'], component_name='main')
+    ...     person=no_priv['self_link'], component_name='main'))
     HTTP/1.1 201 Created
     ...
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     cprov_archive['self_link'], 'deleteComponentUploader', {},
     ...     person=no_priv['self_link'],
-    ...     component_name='main')
+    ...     component_name='main'))
     HTTP/1.1 200 Ok
     ...
 
@@ -551,7 +551,7 @@
     >>> response = cprov_webservice.named_post(
     ...     cprov_archive['self_link'], 'newComponentUploader', {},
     ...     person=name12['self_link'], component_name='restricted')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 400 Bad Request
     ...
     Component for PPAs should be 'main'
@@ -591,13 +591,13 @@
     ...     ubuntu['main_archive_link'], 'newQueueAdmin', {},
     ...     person=name12['self_link'],
     ...     component_name='partner')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
     >>> new_permission = ubuntu_owner_webservice.get(
     ...     response.getHeader('Location')).jsonBody()
-    >>> print new_permission['self_link']
+    >>> print(new_permission['self_link'])
     http://.../ubuntu/+archive/primary/+queue-admin/name12?type=component&item=partner
 
     >>> show_components_for_admin(name12)
@@ -609,10 +609,10 @@
 
 deleteQueueAdmin removes that permission.
 
-    >>> print ubuntu_owner_webservice.named_post(
+    >>> print(ubuntu_owner_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'deleteQueueAdmin', {},
     ...     person=name12['self_link'],
-    ...     component_name='partner')
+    ...     component_name='partner'))
     HTTP/1.1 200 Ok
     ...
 
@@ -637,9 +637,9 @@
 
 Passing a bad pocket name results in an error:
 
-    >>> print cjwatson_webservice.named_get(
+    >>> print(cjwatson_webservice.named_get(
     ...     ubuntu_devel['main_archive_link'], 'getUploadersForPocket',
-    ...     api_version='devel', pocket='badpocket')
+    ...     api_version='devel', pocket='badpocket'))
     HTTP/1.1 400 Bad Request
     ...
     pocket: Invalid value "badpocket". Acceptable values are: ...
@@ -650,13 +650,13 @@
     ...     ubuntu_devel['main_archive_link'], 'newPocketUploader', {},
     ...     api_version='devel', person=name12['self_link'],
     ...     pocket='Proposed')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
     >>> new_permission = user_webservice.get(
     ...     response.getHeader('Location')).jsonBody()
-    >>> print new_permission['self_link']
+    >>> print(new_permission['self_link'])
     http://.../ubuntu/+archive/primary/+upload/name12?type=pocket&item=PROPOSED
 
     >>> show_pocket_permissions('Proposed')
@@ -676,10 +676,10 @@
 
 deletePocketUploader removes that permission:
 
-    >>> print ubuntu_owner_webservice.named_post(
+    >>> print(ubuntu_owner_webservice.named_post(
     ...     ubuntu_devel['main_archive_link'], 'deletePocketUploader', {},
     ...     api_version='devel', person=name12['self_link'],
-    ...     pocket='Proposed')
+    ...     pocket='Proposed'))
     HTTP/1.1 200 Ok
     ...
 
@@ -729,13 +729,13 @@
     ...     ubuntu_devel['main_archive_link'], 'newPocketQueueAdmin', {},
     ...     api_version='devel', person=name12['self_link'],
     ...     pocket='Security')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
     >>> new_permission = ubuntu_owner_webservice.get(
     ...     response.getHeader('Location')).jsonBody()
-    >>> print new_permission['self_link']
+    >>> print(new_permission['self_link'])
     http://.../ubuntu/+archive/primary/+queue-admin/name12?type=pocket&item=SECURITY
 
     >>> show_pockets_for_admin(name12)
@@ -752,7 +752,7 @@
     ...         ubuntu_devel['main_archive_link'], 'newPocketQueueAdmin', {},
     ...         api_version='devel', person=ubuntu_owner_ws['self_link'],
     ...         pocket='Security', distroseries=series['self_link'])
-    ...     print response
+    ...     print(response)
     ...     new_permissions.append(ubuntu_owner_webservice.get(
     ...         response.getHeader('Location')).jsonBody())
     HTTP/1.1 201 Created
@@ -760,9 +760,9 @@
     HTTP/1.1 201 Created
     ...
 
-    >>> print new_permissions[0]['self_link']
+    >>> print(new_permissions[0]['self_link'])
     http://.../ubuntu/+archive/primary/+queue-admin/ubuntu-owner?type=pocket&item=SECURITY&series=hoary
-    >>> print new_permissions[1]['self_link']
+    >>> print(new_permissions[1]['self_link'])
     http://.../ubuntu/+archive/primary/+queue-admin/ubuntu-owner?type=pocket&item=SECURITY&series=grumpy
 
     >>> show_pockets_for_admin(ubuntu_owner_ws)
@@ -771,17 +771,17 @@
 
 deletePocketQueueAdmin removes these permissions.
 
-    >>> print ubuntu_owner_webservice.named_post(
+    >>> print(ubuntu_owner_webservice.named_post(
     ...     ubuntu_devel['main_archive_link'], 'deletePocketQueueAdmin', {},
     ...     api_version='devel', person=name12['self_link'],
-    ...     pocket='Security')
+    ...     pocket='Security'))
     HTTP/1.1 200 Ok
     ...
     >>> for series in hoary, grumpy:
-    ...     print ubuntu_owner_webservice.named_post(
+    ...     print(ubuntu_owner_webservice.named_post(
     ...         ubuntu_devel['main_archive_link'], 'deletePocketQueueAdmin',
     ...         {}, api_version='devel', person=ubuntu_owner_ws['self_link'],
-    ...         pocket='Security', distroseries=series['self_link'])
+    ...         pocket='Security', distroseries=series['self_link']))
     HTTP/1.1 200 Ok
     ...
     HTTP/1.1 200 Ok
@@ -803,7 +803,7 @@
     >>> missing_type_url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?item=firefox')
     >>> this_will_fail = webservice.get(missing_type_url)
-    >>> print this_will_fail
+    >>> print(this_will_fail)
     HTTP/1.1 404 Not Found
     ...
 
@@ -813,7 +813,7 @@
     >>> wrong_type_url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset&item=firefox&type=Integer')
     >>> this_will_fail = webservice.get(missing_type_url)
-    >>> print this_will_fail
+    >>> print(this_will_fail)
     HTTP/1.1 404 Not Found
     ...
 
@@ -822,7 +822,7 @@
     >>> missing_item_url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset')
     >>> this_will_fail = webservice.get(missing_type_url)
-    >>> print this_will_fail
+    >>> print(this_will_fail)
     HTTP/1.1 404 Not Found
     ...
 
@@ -832,7 +832,7 @@
     >>> wrong_type_url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset&item=firefox&item=vapourware')
     >>> this_will_fail = webservice.get(missing_type_url)
-    >>> print this_will_fail
+    >>> print(this_will_fail)
     HTTP/1.1 404 Not Found
     ...
 
@@ -846,7 +846,7 @@
     >>> build_counters = webservice.named_get(
     ...     ubuntu['main_archive_link'], 'getBuildCounters').jsonBody()
     >>> for key, val in build_counters.items():
-    ...     print "%s: %s" % (key, val)
+    ...     print("%s: %s" % (key, val))
     failed: 5
     superseded: 3
     total: 18
@@ -859,7 +859,7 @@
     ...     ubuntu['main_archive_link'], 'getBuildCounters',
     ...     include_needsbuild=False).jsonBody()
     >>> for key, val in build_counters.items():
-    ...     print "%s: %s" % (key, val)
+    ...     print("%s: %s" % (key, val))
     failed: 5
     superseded: 3
     total: 17
@@ -874,11 +874,11 @@
 
     >>> response = webservice.named_get(
     ...     cprov_archive['self_link'], 'getPublishedSources')
-    >>> print response.getheader('status')
+    >>> print(response.getheader('status'))
     200 Ok
     >>> response = webservice.named_get(
     ...     cprov_archive['self_link'], 'getPublishedBinaries')
-    >>> print response.getheader('status')
+    >>> print(response.getheader('status'))
     200 Ok
 
 If either method is called with the version parameter, the name must
@@ -887,26 +887,26 @@
 
     >>> response = webservice.named_get(
     ...     cprov_archive['self_link'], 'getPublishedSources', version='1.0')
-    >>> print response.getheader('status')
+    >>> print(response.getheader('status'))
     400 Bad Request
     >>> response = webservice.named_get(
     ...     cprov_archive['self_link'], 'getPublishedBinaries',
     ...     version='1.0')
-    >>> print response.getheader('status')
+    >>> print(response.getheader('status'))
     400 Bad Request
 
 We don't have to specify any filters when getting published sources:
 
     >>> response = webservice.named_get(
     ...     cprov_archive['self_link'], 'getPublishedSources').jsonBody()
-    >>> print response['total_size']
+    >>> print(response['total_size'])
     3
 
 We can filter getPublishedSources() by component. All of the publishing
 histories we got previously were in 'main':
 
     >>> for entry in response['entries']:
-    ...     print entry['component_name']
+    ...     print(entry['component_name'])
     main
     main
     main
@@ -970,22 +970,22 @@
 using user_webservice, which has no privileges, and trying to copy to
 the Ubuntu main archive:
 
-    >>> print user_webservice.named_post(
+    >>> print(user_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'syncSource', {},
     ...     source_name='package3', version='1.0',
     ...     from_archive=cprov_archive['self_link'], to_pocket='release',
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 401 Unauthorized
     ...
 
 When accessed via Colin's key that can perform writes, the API will
 respond positively.
 
-    >>> print cjwatson_webservice.named_post(
+    >>> print(cjwatson_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'syncSource', {},
     ...     source_name='package3', version='1.0',
     ...     from_archive=cprov_archive['self_link'], to_pocket='release',
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 200 Ok
     ...
 
@@ -993,11 +993,11 @@
 PPA. The 'admin_write' key created for Colin isn't allowed to modify
 Celso's PPA.
 
-    >>> print cjwatson_webservice.named_post(
+    >>> print(cjwatson_webservice.named_post(
     ...     cprov_archive['self_link'], 'syncSource', {},
     ...     source_name='package1', version='1.0',
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -1007,11 +1007,11 @@
     >>> cprov_webservice = webservice_for_person(
     ...     cprov, permission=OAuthPermission.WRITE_PUBLIC)
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     cprov_archive['self_link'], 'syncSource', {},
     ...     source_name='package1', version='1.0',
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 200 Ok
     ...
 
@@ -1020,11 +1020,11 @@
 "synchronised" to the context archive.  If a particular version already
 exists then nothing is copied.
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     cprov_archive['self_link'], 'syncSources', {},
     ...     source_names=['package1', 'package2'],
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
-    ...     to_series="warty")
+    ...     to_series="warty"))
     HTTP/1.1 200 Ok
     ...
 
@@ -1036,7 +1036,7 @@
     ...     source_names=['package1', 'package2'],
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
     ...     to_series="warty")
-    >>> print already_copied
+    >>> print(already_copied)
     HTTP/1.1 200 Ok
     ...
 
@@ -1047,7 +1047,7 @@
 syncSources is invoked directly by the client, and any problems are
 the client's fault. Therefore, there's no need to record an OOPS.
 
-    >>> print already_copied.getheader('X-Lazr-Oopsid')
+    >>> print(already_copied.getheader('X-Lazr-Oopsid'))
     None
 
 'syncSources' behaves trasactionally, i.e. it will only synchronise
@@ -1065,11 +1065,11 @@
 'package1' has no binaries to be copied, so when we attempt to copy
 'allowed' and 'package1' with binaries an error is returned.
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     cprov_archive['self_link'], 'syncSources', {},
     ...     source_names=['allowed', 'package1'],
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
-    ...     to_series="warty", include_binaries=True)
+    ...     to_series="warty", include_binaries=True))
     HTTP/1.1 400 Bad Request
     ...
     package1 1.1 in hoary (source has no binaries to be copied)
@@ -1085,19 +1085,19 @@
 Keys with insufficient permissions on Celso's PPA context are not
 allowed to call the method at all.
 
-    >>> print user_webservice.named_post(
+    >>> print(user_webservice.named_post(
     ...     cprov_archive['self_link'], 'syncSources', {},
     ...     source_names=['package1', 'package2'],
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
-    ...     to_series="warty")
+    ...     to_series="warty"))
     HTTP/1.1 401 Unauthorized
     ...
 
-    >>> print cjwatson_webservice.named_post(
+    >>> print(cjwatson_webservice.named_post(
     ...     cprov_archive['self_link'], 'syncSources', {},
     ...     source_names=['package1', 'package2'],
     ...     from_archive=ubuntu['main_archive_link'], to_pocket='release',
-    ...     to_series="warty")
+    ...     to_series="warty"))
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -1130,7 +1130,7 @@
 
 Attempting to modify this flag without the necessary permissions will fail.
 
-    >>> print modify_archive(user_webservice, mark_archive)
+    >>> print(modify_archive(user_webservice, mark_archive))
     HTTP/1.1 400 Bad Request
     ...
     http_etag: You tried to modify a read-only attribute.
@@ -1143,7 +1143,7 @@
 describes the maximum size, in MiB, allowed for the archive.
 
     >>> mark_archive = webservice.get("/~mark/+archive/ubuntu/ppa").jsonBody()
-    >>> print mark_archive['authorized_size']
+    >>> print(mark_archive['authorized_size'])
     1024
 
 Modifying the authorized_size attribute through the API is not allowed except
@@ -1152,14 +1152,14 @@
     >>> mark_archive['authorized_size'] = 4096
     >>> response = modify_archive(admin_webservice, mark_archive)
     >>> mark_archive = webservice.get("/~mark/+archive/ubuntu/ppa").jsonBody()
-    >>> print mark_archive['authorized_size']
+    >>> print(mark_archive['authorized_size'])
     4096
 
 Attempting to modify this flag without the necessary permissions will fail.
 
     >>> mark_archive = webservice.get("/~mark/+archive/ubuntu/ppa").jsonBody()
     >>> mark_archive['authorized_size'] = 1024
-    >>> print modify_archive(user_webservice, mark_archive)
+    >>> print(modify_archive(user_webservice, mark_archive))
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'authorized_size', 'launchpad.Admin')
@@ -1251,7 +1251,7 @@
     >>> response = mark_webservice.named_post(
     ...     mark_archive['self_link'], 'newSubscription',
     ...     subscriber=cprov_archive['owner_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 400 Bad Request
     ...
     Only private archives can have subscriptions.
@@ -1265,11 +1265,11 @@
     ...     cprov_private_ppa['self_link'], 'newSubscription',
     ...     subscriber=mark['self_link'])
 
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
-    >>> print response.getHeader('Location')
+    >>> print(response.getHeader('Location'))
     http://.../~cprov/+archive/ubuntu/p3a/+subscriptions/mark
 
 We publish a subset of the IArchiveSubscriber attributes.
@@ -1292,7 +1292,7 @@
 
     >>> response = user_webservice.get(
     ...     response.getHeader('Location'))
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -1303,7 +1303,7 @@
     >>> response = user_webservice.named_post(
     ...     cprov_archive['self_link'], 'newSubscription',
     ...     subscriber=cprov_private_ppa['owner_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -1313,7 +1313,7 @@
     >>> response = cprov_webservice.named_post(
     ...     cprov_private_ppa['self_link'], 'newSubscription',
     ...     subscriber=mark['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 400 Bad Request
     ...
     Mark Shuttleworth already has a current subscription
@@ -1324,7 +1324,7 @@
 
     >>> response = cprov_webservice.get(
     ...     cprov_private_ppa['self_link'] + '/+subscriptions/dave')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
 
@@ -1342,7 +1342,7 @@
     >>> pubpriv_archive = webservice.get(
     ...     "/~cprov/+archive/ubuntu/pubpriv").jsonBody()
     >>> pubpriv_archive['private'] = True
-    >>> print modify_archive(user_webservice, pubpriv_archive)
+    >>> print(modify_archive(user_webservice, pubpriv_archive))
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'private', 'launchpad.Admin')
@@ -1353,7 +1353,7 @@
     >>> logout()
     >>> ppa_admin_webservice = webservice_for_person(
     ...     ppa_admin, permission=OAuthPermission.WRITE_PRIVATE)
-    >>> print modify_archive(ppa_admin_webservice, pubpriv_archive)
+    >>> print(modify_archive(ppa_admin_webservice, pubpriv_archive))
     HTTP/1.1 209 Content Returned
     ...
     >>> webservice.get(
@@ -1370,11 +1370,11 @@
 We use `syncSource` to copy 'foocomm - 1.0-1' source from Celso's
 private PPA to the ubuntu primary archive.
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'syncSource', {},
     ...     source_name='foocomm', version='1.0-1', to_pocket='release',
     ...     from_archive=cprov_private_ppa['self_link'],
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 200 Ok
     ...
 
@@ -1388,22 +1388,22 @@
     ...     PackagePublishingStatus.PUBLISHED)
     >>> logout()
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'syncSources', {},
     ...     source_names=['foocomm'], to_pocket='release',
     ...     from_archive=cprov_private_ppa['self_link'],
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 200 Ok
     ...
 
 Although if we try to copy an old version, by repeating the copy an
 error is returned.
 
-    >>> print cprov_webservice.named_post(
+    >>> print(cprov_webservice.named_post(
     ...     ubuntu['main_archive_link'], 'syncSource', {},
     ...     source_name='foocomm', version='1.0-2', to_pocket='release',
     ...     from_archive=cprov_private_ppa['self_link'],
-    ...     to_series="hoary")
+    ...     to_series="hoary"))
     HTTP/1.1 400 Bad Request
     ...
     foocomm 1.0-2 in hoary
@@ -1418,7 +1418,7 @@
     >>> private_archive = cprov_webservice.get(
     ...     cprov_private_ppa['self_link']).jsonBody()
     >>> private_archive['suppress_subscription_notifications'] = True
-    >>> print modify_archive(cprov_webservice, private_archive)
+    >>> print(modify_archive(cprov_webservice, private_archive))
     HTTP/1.1 209 ...
     ...
 
@@ -1472,21 +1472,21 @@
 
 Crafting a URL to a non-dependency 404s:
 
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/2')
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/2'))
     HTTP/1.1 404 Not Found
     ...
 
 A 404 also occurs if we ask for an archive that doesn't exist.
 
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/123456')
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/123456'))
     HTTP/1.1 404 Not Found
     ...
 
 And even if we ask for a non-integral archive ID.
 
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/foo')
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/foo'))
     HTTP/1.1 404 Not Found
     ...

=== modified file 'lib/lp/soyuz/stories/webservice/xx-archivedependency.txt'
--- lib/lp/soyuz/stories/webservice/xx-archivedependency.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/webservice/xx-archivedependency.txt	2018-07-01 09:01:37 +0000
@@ -36,29 +36,29 @@
 
 Any user can retrieve a public PPA's dependencies.
 
-    >>> print user_webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/dependencies')
+    >>> print(user_webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/dependencies'))
     HTTP/1.1 200 Ok
     ...
 
-    >>> print user_webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/1')
+    >>> print(user_webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+dependency/1'))
     HTTP/1.1 200 Ok
     ...
 
 The dependencies of a private archive are private.  Unprivileged users
 can't get a list of the dependencies.
 
-    >>> print user_webservice.get(
-    ...     '/~cprov/+archive/ubuntu/p3a/dependencies')
+    >>> print(user_webservice.get(
+    ...     '/~cprov/+archive/ubuntu/p3a/dependencies'))
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'dependencies', 'launchpad.SubscriberView')
 
 Nor can said user craft a URL to a dependency.
 
-    >>> print user_webservice.get(
-    ...     '/~cprov/+archive/ubuntu/p3a/+dependency/1')
+    >>> print(user_webservice.get(
+    ...     '/~cprov/+archive/ubuntu/p3a/+dependency/1'))
     HTTP/1.1 401 Unauthorized
     ...
     (<Archive at ...>, 'getArchiveDependency', 'launchpad.View')
@@ -69,12 +69,12 @@
     >>> from lp.services.webapp.interfaces import OAuthPermission
     >>> cprov_webservice = webservice_for_person(
     ...     cprov_db, permission=OAuthPermission.WRITE_PRIVATE)
-    >>> print cprov_webservice.get(
-    ...     '/~cprov/+archive/ubuntu/p3a/dependencies')
+    >>> print(cprov_webservice.get(
+    ...     '/~cprov/+archive/ubuntu/p3a/dependencies'))
     HTTP/1.1 200 Ok
     ...
-    >>> print cprov_webservice.get(
-    ...     '/~cprov/+archive/ubuntu/p3a/+dependency/1')
+    >>> print(cprov_webservice.get(
+    ...     '/~cprov/+archive/ubuntu/p3a/+dependency/1'))
     HTTP/1.1 200 Ok
     ...
 
@@ -82,25 +82,25 @@
 
     >>> mark_ppa = cprov_webservice.get(
     ...     '/~mark/+archive/ubuntu/ppa').jsonBody()
-    >>> print cprov_webservice.patch(
+    >>> print(cprov_webservice.patch(
     ...     '/~cprov/+archive/ubuntu/ppa/+dependency/1', 'application/json',
-    ...     simplejson.dumps({'archive_link': mark_ppa['self_link']}))
+    ...     simplejson.dumps({'archive_link': mark_ppa['self_link']})))
     HTTP/1.1 400 Bad Request
     ...
     archive_link: You tried to modify a read-only attribute.
     <BLANKLINE>
 
-    >>> print cprov_webservice.patch(
+    >>> print(cprov_webservice.patch(
     ...     '/~cprov/+archive/ubuntu/ppa/+dependency/1', 'application/json',
-    ...     simplejson.dumps({'dependency_link': mark_ppa['self_link']}))
+    ...     simplejson.dumps({'dependency_link': mark_ppa['self_link']})))
     HTTP/1.1 400 Bad Request
     ...
     dependency_link: You tried to modify a read-only attribute.
     <BLANKLINE>
 
-    >>> print cprov_webservice.patch(
+    >>> print(cprov_webservice.patch(
     ...     '/~cprov/+archive/ubuntu/ppa/+dependency/1', 'application/json',
-    ...     simplejson.dumps({'pocket': 'Security'}))
+    ...     simplejson.dumps({'pocket': 'Security'})))
     HTTP/1.1 400 Bad Request
     ...
     pocket: You tried to modify a read-only attribute.

=== modified file 'lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt'
--- lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt	2015-04-09 05:16:37 +0000
+++ lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt	2018-07-01 09:01:37 +0000
@@ -12,7 +12,7 @@
     >>> def print_publications(pubs):
     ...     for display_name in sorted(
     ...         entry['display_name'] for entry in pubs['entries']):
-    ...         print display_name
+    ...         print(display_name)
 
     >>> print_publications(pubs)
     mozilla-firefox 1.0 in warty hppa
@@ -120,7 +120,7 @@
     >>> cprov_private_ppa = webservice.get("/~cprov/+archive/ubuntu/p3a").jsonBody()
     >>> cprov_bins_response = webservice.named_get(
     ...     cprov_private_ppa['self_link'], 'getPublishedBinaries')
-    >>> print cprov_bins_response
+    >>> print(cprov_bins_response)
     HTTP/1.1 200 Ok
     ...
 
@@ -128,7 +128,7 @@
 
     >>> response = user_webservice.named_get(
     ...     cprov_private_ppa['self_link'], 'getPublishedBinaries')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -138,7 +138,7 @@
     >>> pubs = cprov_bins_response.jsonBody()
     >>> private_publication_url = pubs['entries'][0]['self_link']
     >>> response = user_webservice.get(private_publication_url)
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -213,33 +213,33 @@
 
 But other URLs result in a 404.
 
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/moz')
-    HTTP/1.1 404 Not Found
-    ...
-
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/phoenix/1.0/hppa/2010-02-23/unknown')
-    HTTP/1.1 404 Not Found
-    ...
-
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.1/hppa/2010-02-23/unknown')
-    HTTP/1.1 404 Not Found
-    ...
-
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.0/foo/2010-02-23/unknown')
-    HTTP/1.1 404 Not Found
-    ...
-
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-25/unknown')
-    HTTP/1.1 404 Not Found
-    ...
-
-    >>> print webservice.get(
-    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-23/XX')
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/moz'))
+    HTTP/1.1 404 Not Found
+    ...
+
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/phoenix/1.0/hppa/2010-02-23/unknown'))
+    HTTP/1.1 404 Not Found
+    ...
+
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.1/hppa/2010-02-23/unknown'))
+    HTTP/1.1 404 Not Found
+    ...
+
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.0/foo/2010-02-23/unknown'))
+    HTTP/1.1 404 Not Found
+    ...
+
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-25/unknown'))
+    HTTP/1.1 404 Not Found
+    ...
+
+    >>> print(webservice.get(
+    ...     '/~cprov/+archive/ubuntu/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-23/XX'))
     HTTP/1.1 404 Not Found
     ...
 
@@ -270,9 +270,9 @@
     >>> pubs = webservice.named_get(
     ...     cprov_archive_devel["self_link"], "getPublishedBinaries",
     ...     api_version="devel", binary_name="testoverrides").jsonBody()
-    >>> print pubs["entries"][0]["section_name"]
+    >>> print(pubs["entries"][0]["section_name"])
     base
-    >>> print pubs["entries"][0]["priority_name"]
+    >>> print(pubs["entries"][0]["priority_name"])
     STANDARD
     >>> package = pubs["entries"][0]["self_link"]
 
@@ -281,7 +281,7 @@
     >>> response = webservice.named_post(
     ...     package, "changeOverride", api_version="devel",
     ...     new_section="admin", new_priority="optional")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -290,7 +290,7 @@
     >>> response = cprov_webservice.named_post(
     ...     package, "changeOverride", api_version="devel",
     ...     new_section="admin", new_priority="optional")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -299,7 +299,7 @@
     >>> pubs = webservice.named_get(
     ...     cprov_archive["self_link"], "getPublishedBinaries",
     ...     binary_name="testoverrides").jsonBody()
-    >>> print pubs["entries"][0]["section_name"]
+    >>> print(pubs["entries"][0]["section_name"])
     admin
-    >>> print pubs["entries"][0]["priority_name"]
+    >>> print(pubs["entries"][0]["priority_name"])
     OPTIONAL

=== modified file 'lib/lp/soyuz/stories/webservice/xx-builders.txt'
--- lib/lp/soyuz/stories/webservice/xx-builders.txt	2014-06-17 12:39:51 +0000
+++ lib/lp/soyuz/stories/webservice/xx-builders.txt	2018-07-01 09:01:37 +0000
@@ -12,7 +12,7 @@
 Iterating over the collection is possible:
 
     >>> for builder in builders:
-    ...     print builder
+    ...     print(builder)
     http://api.launchpad.dev/devel/builders/bob
     http://api.launchpad.dev/devel/builders/frog
 
@@ -24,7 +24,7 @@
 Each builder has a number of properties exposed:
 
     >>> for attribute in bob.lp_attributes:
-    ...     print attribute
+    ...     print(attribute)
     self_link
     ...
     active
@@ -54,7 +54,7 @@
 Our "bob" builder was retrieved using the no-priv user, and no-priv does not
 have permission to save changes:
 
-    >>> print bob.active
+    >>> print(bob.active)
     True
     >>> bob.active = False
     >>> bob.lp_save()

=== modified file 'lib/lp/soyuz/stories/webservice/xx-builds.txt'
--- lib/lp/soyuz/stories/webservice/xx-builds.txt	2016-03-03 18:41:27 +0000
+++ lib/lp/soyuz/stories/webservice/xx-builds.txt	2018-07-01 09:01:37 +0000
@@ -154,10 +154,10 @@
     >>> builds = webservice.named_get(
     ...     source_pub['self_link'], 'getBuilds').jsonBody()
 
-    >>> print builds['entries'][0]['log_url']
+    >>> print(builds['entries'][0]['log_url'])
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+build/26/+files/...
 
-    >>> print builds['entries'][0]['upload_log_url']
+    >>> print(builds['entries'][0]['upload_log_url'])
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+build/26/+files/...
 
 Re-trying builds
@@ -172,8 +172,8 @@
 
 Plain users have no permission to call retry:
 
-    >>> print user_webservice.named_post(
-    ...     a_build['self_link'], 'retry')
+    >>> print(user_webservice.named_post(
+    ...     a_build['self_link'], 'retry'))
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -190,8 +190,8 @@
 
     >>> admin_webservice = webservice_for_person(
     ...     admin_person, permission=OAuthPermission.WRITE_PUBLIC)
-    >>> print admin_webservice.named_post(
-    ...     a_build['self_link'], 'retry')
+    >>> print(admin_webservice.named_post(
+    ...     a_build['self_link'], 'retry'))
     HTTP/1.1 200 Ok
     ...
 
@@ -199,8 +199,8 @@
 
     >>> cprov_webservice = webservice_for_person(
     ...     cprov, permission=OAuthPermission.WRITE_PUBLIC)
-    >>> print cprov_webservice.named_post(
-    ...     a_build['self_link'], 'retry')
+    >>> print(cprov_webservice.named_post(
+    ...     a_build['self_link'], 'retry'))
     HTTP/1.1 500 Internal Server Error
     ...
     AssertionError: Build ... cannot be retried
@@ -212,7 +212,7 @@
 
     >>> builds = webservice.named_get(
     ...     source_pub['self_link'], 'getBuilds').jsonBody()
-    >>> print builds['entries'][0]['can_be_retried']
+    >>> print(builds['entries'][0]['can_be_retried'])
     False
 
 
@@ -223,8 +223,8 @@
 custom operation.  However, the caller must be a member of the buildd admins
 team.
 
-    >>> print user_webservice.named_post(
-    ...     a_build['self_link'], 'rescore', score=1000)
+    >>> print(user_webservice.named_post(
+    ...     a_build['self_link'], 'rescore', score=1000))
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -238,15 +238,15 @@
     True
 
     >>> logout()
-    >>> print cprov_webservice.named_post(
-    ...     a_build['self_link'], 'rescore', score=1000)
+    >>> print(cprov_webservice.named_post(
+    ...     a_build['self_link'], 'rescore', score=1000))
     HTTP/1.1 200 Ok
     ...
 
 The job has been rescored
 
     >>> updated_build = webservice.get(a_build['self_link']).jsonBody()
-    >>> print updated_build['score']
+    >>> print(updated_build['score'])
     1000
 
 If the build cannot be retried, then a 400 code is returned.  Let's
@@ -257,8 +257,8 @@
     >>> build.updateStatus(BuildStatus.FAILEDTOUPLOAD)
     >>> logout()
 
-    >>> print cprov_webservice.named_post(
-    ...     a_build['self_link'], 'rescore', score=1000)
+    >>> print(cprov_webservice.named_post(
+    ...     a_build['self_link'], 'rescore', score=1000))
     HTTP/1.1 400 Bad Request
     ...
     Build cannot be rescored.

=== modified file 'lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt'
--- lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt	2013-12-13 12:32:24 +0000
+++ lib/lp/soyuz/stories/webservice/xx-distroarchseries.txt	2018-07-01 09:01:37 +0000
@@ -6,12 +6,12 @@
 
     >>> distros = webservice.get("/distros").jsonBody()
     >>> ubuntu = distros['entries'][0]
-    >>> print ubuntu['self_link']
+    >>> print(ubuntu['self_link'])
     http://.../ubuntu
 
     >>> current_series = webservice.get(
     ...     ubuntu['current_series_link']).jsonBody()
-    >>> print current_series['self_link']
+    >>> print(current_series['self_link'])
     http://.../ubuntu/hoary
 
 We'll first set up a buildd chroot, so we can check that its URL is

=== modified file 'lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt'
--- lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt	2014-07-24 09:37:03 +0000
+++ lib/lp/soyuz/stories/webservice/xx-hasbuildrecords.txt	2018-07-01 09:01:37 +0000
@@ -22,7 +22,7 @@
     >>> def print_builds(builds):
     ...     for entry in sorted(builds['entries'],
     ...                         key=lambda entry:entry.get('title')):
-    ...         print entry['title']
+    ...         print(entry['title'])
 
 
 Filtering builds

=== modified file 'lib/lp/soyuz/stories/webservice/xx-packageset.txt'
--- lib/lp/soyuz/stories/webservice/xx-packageset.txt	2015-10-06 06:48:01 +0000
+++ lib/lp/soyuz/stories/webservice/xx-packageset.txt	2018-07-01 09:01:37 +0000
@@ -33,7 +33,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'umbrella', description=u'Contains all source packages',
     ...     owner=name12['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -45,7 +45,7 @@
     ...     IPackagesetSet)
     >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
     >>> ps_factory = getUtility(IPackagesetSet)
-    >>> print ps_factory.getByName(ubuntu.currentseries, u'umbrella').name
+    >>> print(ps_factory.getByName(ubuntu.currentseries, u'umbrella').name)
     umbrella
 
 Can we access it via the webservice API as well?
@@ -53,7 +53,7 @@
     >>> logout()
     >>> umbrella = webservice.get(
     ...     "/package-sets/ubuntu/hoary/umbrella").jsonBody()
-    >>> print umbrella['self_link']
+    >>> print(umbrella['self_link'])
     http://api.launchpad.dev/beta/package-sets/ubuntu/hoary/umbrella
 
 `PackageSet`s can be looked up by name.
@@ -61,7 +61,7 @@
     >>> response = webservice.named_get(
     ...     '/package-sets', 'getByName', {}, distroseries='/ubuntu/hoary',
     ...     name=u'umbrella')
-    >>> print response.jsonBody()['self_link']
+    >>> print(response.jsonBody()['self_link'])
     http://api.launchpad.dev/beta/package-sets/ubuntu/hoary/umbrella
 
 When a `PackageSet` cannot be found, an error is returned.
@@ -69,7 +69,7 @@
     >>> response = webservice.named_get(
     ...     '/package-sets', 'getByName', {}, distroseries='/ubuntu/hoary',
     ...     name=u'not-found')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
     No such package set (in the specified distro series): 'not-found'.
@@ -78,7 +78,7 @@
 
     >>> response = webservice.get(
     ...     "/package-sets/ubuntu/lucid-plus-1/umbrella/+pwn")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
 
@@ -88,7 +88,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'shortlived', description=u'An ephemeral packageset',
     ...     owner=name12['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -102,7 +102,7 @@
     >>> response = webservice.patch(
     ...     '/package-sets/ubuntu/hoary/shortlived', 'application/json',
     ...     dumps(patch))
-    >>> print response
+    >>> print(response)
     HTTP/1.1 301 Moved Permanently
     ...
 
@@ -110,7 +110,7 @@
 
     >>> response = webservice.delete(
     ...     '/package-sets/ubuntu/hoary/renamed', {}, api_version='devel')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -122,7 +122,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'addSources', {},
     ...     names=[spn.name for spn in all_spns])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -132,7 +132,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'addSources', {},
     ...     names=[u'does-not-exist'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     null
@@ -140,7 +140,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'removeSources', {},
     ...     names=[u'does-not-exist'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     null
@@ -149,7 +149,7 @@
 
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'getSourcesIncluded', {})
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["a52dec",
@@ -177,7 +177,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'removeSources', {},
     ...     names=["foobar", "iceweasel"])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -186,7 +186,7 @@
 
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'getSourcesIncluded', {})
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["a52dec",
@@ -211,7 +211,7 @@
     >>> def print_payload(response):
     ...     body = response.jsonBody()
     ...     for entry in body['entries']:
-    ...         print entry['self_link']
+    ...         print(entry['self_link'])
 
     >>> response = anon_webservice.get("/package-sets/")
     >>> print_payload(response)
@@ -222,7 +222,7 @@
 
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'setsIncluded', {})
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     {"total_size": 0, "start": 0, "entries": []}
@@ -233,7 +233,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'gnome', description=u'Contains all gnome packages',
     ...     owner=name12['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -241,7 +241,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'mozilla', description=u'Contains all mozilla packages',
     ...     owner=name12['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -255,10 +255,10 @@
     >>> mozilla = webservice.named_get(
     ...     '/package-sets', 'getByName', {}, distroseries='/ubuntu/hoary',
     ...     name=u'mozilla').jsonBody()
-    >>> print mozilla['distroseries_link']
+    >>> print(mozilla['distroseries_link'])
     http://api.launchpad.dev/beta/ubuntu/hoary
 
-    >>> print mozilla['self_link']
+    >>> print(mozilla['self_link'])
     http://api.launchpad.dev/beta/package-sets/ubuntu/hoary/mozilla
 
 A collection of package sets belonging to a given distro series can be
@@ -268,7 +268,7 @@
     ...     '/package-sets', 'getBySeries', {},
     ...     distroseries=mozilla['distroseries_link']).jsonBody()
     >>> for entry in packagesets["entries"]:
-    ...     print "{entry[name]}: {entry[description]}".format(entry=entry)
+    ...     print("{entry[name]}: {entry[description]}".format(entry=entry))
     gnome: Contains all gnome packages
     mozilla: Contains all mozilla packages
     umbrella: Contains all source packages
@@ -281,7 +281,7 @@
 that exists already.
 
     >>> grumpy = webservice.get("/ubuntu/grumpy").jsonBody()
-    >>> print grumpy['self_link']
+    >>> print(grumpy['self_link'])
     http://api.launchpad.dev/beta/ubuntu/grumpy
 
 We are adding a new 'mozilla' package set to the 'grumpy' distro series and
@@ -292,17 +292,17 @@
     ...     name=u'mozilla', owner=name12['self_link'],
     ...     description=u'Contains all mozilla packages in grumpy',
     ...     related_set=mozilla['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
     >>> grumpy_mozilla = webservice.named_get(
     ...     '/package-sets', 'getByName', {}, name=u'mozilla',
     ...     distroseries=grumpy['self_link']).jsonBody()
-    >>> print grumpy_mozilla['distroseries_link']
+    >>> print(grumpy_mozilla['distroseries_link'])
     http://api.launchpad.dev/beta/ubuntu/grumpy
 
-    >>> print grumpy_mozilla['self_link']
+    >>> print(grumpy_mozilla['self_link'])
     http://api.launchpad.dev/beta/package-sets/ubuntu/grumpy/mozilla
 
     >>> response = webservice.named_get(
@@ -320,7 +320,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'firefox', description=u'Contains all firefox packages',
     ...     owner=name12['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -328,7 +328,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'thunderbird', owner=name12['self_link'],
     ...     description=u'Contains all thunderbird packages')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -336,7 +336,7 @@
     ...     '/package-sets', 'new', {}, distroseries='/ubuntu/hoary',
     ...     name=u'languagepack', owner=name12['self_link'],
     ...     description=u'Contains all languagepack packages')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -347,7 +347,7 @@
     ...     '/package-sets', 'new', {}, distroseries=grumpy['self_link'],
     ...     name=u'languagepack', owner=name12['self_link'],
     ...     description=u'Contains all languagepack packages')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -366,28 +366,28 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/umbrella', 'addSubsets', {},
     ...     names=[u'gnome', u'mozilla'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/gnome', 'addSubsets', {},
     ...     names=[u'languagepack'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'addSubsets', {},
     ...     names=[u'languagepack'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/mozilla', 'addSubsets', {},
     ...     names=[u'firefox', u'thunderbird'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -397,7 +397,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'addSubsets', {},
     ...     names=[u'does-not-exist'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     null
@@ -405,7 +405,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'removeSubsets', {},
     ...     names=[u'does-not-exist'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     null
@@ -457,7 +457,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'removeSubsets', {},
     ...     names=[u'languagepack'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -475,13 +475,13 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/firefox', 'addSources', {},
     ...     names=['at', 'mozilla-firefox', 'language-pack-de'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/firefox', 'getSourcesIncluded', {})
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["at", "language-pack-de", "mozilla-firefox"]
@@ -489,14 +489,14 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'addSources', {},
     ...     names=['at', 'cnews', 'thunderbird', 'language-pack-de'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'getSourcesIncluded',
     ...     {})
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["at", "cnews", "language-pack-de", "thunderbird"]
@@ -526,7 +526,7 @@
     >>> response = webservice.named_get(
     ...     '/package-sets/', 'setsIncludingSource', {},
     ...     sourcepackagename=u'does-not-exist')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
     No such source package: 'does-not-exist'.
@@ -539,7 +539,7 @@
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/firefox', 'getSourcesSharedBy', {},
     ...     other_package_set=thunderbird['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["at", "language-pack-de"]
@@ -549,7 +549,7 @@
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/firefox', 'getSourcesNotSharedBy', {},
     ...     other_package_set=thunderbird['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["mozilla-firefox"]
@@ -559,7 +559,7 @@
     >>> response = webservice.named_get(
     ...     '/package-sets/ubuntu/hoary/thunderbird', 'getSourcesNotSharedBy',
     ...     {}, other_package_set=firefox['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
     ["cnews", "thunderbird"]
@@ -585,7 +585,7 @@
     ...     ubuntu['main_archive_link'], 'newPackagesetUploader', {},
     ...     person=name12['self_link'],
     ...     packageset=firefox['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -613,7 +613,7 @@
     ...     ubuntu['main_archive_link'], 'newPackagesetUploader', {},
     ...     person=name12['self_link'],
     ...     packageset=mozilla['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -642,7 +642,7 @@
     ...     ubuntu['main_archive_link'], 'deletePackagesetUploader', {},
     ...     person=name12['self_link'],
     ...     packageset=mozilla['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -662,7 +662,7 @@
     ...     ubuntu['main_archive_link'], 'newPackagesetUploader', {},
     ...     person=cprov['self_link'],
     ...     packageset=mozilla['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -670,7 +670,7 @@
     ...     ubuntu['main_archive_link'], 'newPackagesetUploader', {},
     ...     person=cprov['self_link'],
     ...     packageset=thunderbird['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -728,10 +728,10 @@
 The following query (note the additional 'distroseries' parameter) is
 thus equivalent:
 
-    >>> print ubuntu['current_series_link']
+    >>> print(ubuntu['current_series_link'])
     http://api.launchpad.dev/beta/ubuntu/hoary
     >>> hoary = webservice.get("/ubuntu/hoary").jsonBody()
-    >>> print hoary['self_link']
+    >>> print(hoary['self_link'])
     http://api.launchpad.dev/beta/ubuntu/hoary
 
     >>> response = webservice.named_get(
@@ -787,7 +787,7 @@
     ...     name=u'thunderbird',
     ...     description=u'Contains all thunderbird packages in grumpy',
     ...     owner=name12['self_link'], related_set=thunderbird['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -801,7 +801,7 @@
     >>> response = webservice.named_post(
     ...     '/package-sets/ubuntu/grumpy/thunderbird', 'addSources', {},
     ...     names=['thunderbird', 'language-pack-de'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -814,7 +814,7 @@
     ...     ubuntu['main_archive_link'], 'newPackagesetUploader', {},
     ...     person=name12['self_link'],
     ...     packageset=grouchy_bird['self_link'])
-    >>> print response
+    >>> print(response)
     HTTP/1.1 201 Created
     ...
 
@@ -876,7 +876,7 @@
     >>> url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset&item=thunderbird')
     >>> response = webservice.get(url)
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
 
@@ -886,7 +886,7 @@
     >>> url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset&item=thunderbird&series=foobar')
     >>> response = webservice.get(url)
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
 
@@ -895,7 +895,7 @@
     >>> url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset&item=thunderbird&series=hoary')
     >>> response = webservice.get(url)
-    >>> print response
+    >>> print(response)
     HTTP/1.1 404 Not Found
     ...
 
@@ -904,9 +904,9 @@
     >>> url = ('/ubuntu/+archive/primary/+upload/name12'
     ...     '?type=packageset&item=thunderbird&series=grumpy')
     >>> permission = webservice.get(url).jsonBody()
-    >>> print permission['package_set_name']
+    >>> print(permission['package_set_name'])
     thunderbird
-    >>> print permission['distro_series_name']
+    >>> print(permission['distro_series_name'])
     grumpy
 
 The user 'cprov' has no upload permission for 'thunderbird' in 'hoary'.

=== modified file 'lib/lp/soyuz/stories/webservice/xx-packageupload.txt'
--- lib/lp/soyuz/stories/webservice/xx-packageupload.txt	2013-04-17 16:40:31 +0000
+++ lib/lp/soyuz/stories/webservice/xx-packageupload.txt	2018-07-01 09:01:37 +0000
@@ -85,7 +85,7 @@
 which is a list of URLs to librarian files:
 
     >>> for entry in uploads['entries']:
-    ...     print entry['display_name']
-    ...     print entry['custom_file_urls']
+    ...     print(entry['display_name'])
+    ...     print(entry['custom_file_urls'])
     custom1, custom2
     [u'http://.../custom1', u'http://.../custom2']

=== modified file 'lib/lp/soyuz/stories/webservice/xx-person-createppa.txt'
--- lib/lp/soyuz/stories/webservice/xx-person-createppa.txt	2014-08-01 08:47:28 +0000
+++ lib/lp/soyuz/stories/webservice/xx-person-createppa.txt	2018-07-01 09:01:37 +0000
@@ -17,19 +17,20 @@
   >>> ppa_owner_webservice = webservice_for_person(
   ...     owner, permission=OAuthPermission.WRITE_PRIVATE)
 
-  >>> print ppa_owner_webservice.named_post(
+  >>> print(ppa_owner_webservice.named_post(
   ...     ppa_owner['self_link'], 'createPPA', {}, distribution='/ubuntu',
-  ...     name='yay', displayname='My shiny new PPA', description='Shinyness!')
+  ...     name='yay', displayname='My shiny new PPA',
+  ...     description='Shinyness!'))
   HTTP/1.1 201 Created
   Status: 201
   ...
   Location: http://api.launchpad.dev/.../+archive/ubuntu/yay
   ...
 
-  >>> print ppa_owner_webservice.named_post(
+  >>> print(ppa_owner_webservice.named_post(
   ...     ppa_owner['self_link'], 'createPPA', {}, name='ubuntu',
   ...     displayname='My shiny new PPA', description='Shinyness!',
-  ...     )
+  ...     ))
   HTTP/1.1 400 Bad Request
   Status: 400 Bad Request
   ...
@@ -45,11 +46,11 @@
 
 They aren't a commercial admin, so they cannot create private PPAs.
 
-  >>> print ppa_owner_webservice.named_post(
+  >>> print(ppa_owner_webservice.named_post(
   ...     ppa_owner['self_link'], 'createPPA', {}, name='whatever',
   ...     displayname='My secret new PPA', description='Secretness!',
   ...     private=True,
-  ...     )
+  ...     ))
   HTTP/1.1 400 Bad Request
   Status: 400
   ...
@@ -71,11 +72,11 @@
 
 Once they have commercial access, they can create private PPAs:
 
-  >>> print ppa_owner_webservice.named_post(
+  >>> print(ppa_owner_webservice.named_post(
   ...     ppa_owner['self_link'], 'createPPA', {}, name='secret',
   ...     displayname='My secret new PPA', description='Secretness!',
   ...     private=True,
-  ...     )
+  ...     ))
   HTTP/1.1 201 Created
   Status: 201
   ...
@@ -98,9 +99,9 @@
 
 It's possible to create PPAs for all sorts of distributions.
 
-  >>> print ppa_owner_webservice.named_post(
+  >>> print(ppa_owner_webservice.named_post(
   ...     ppa_owner['self_link'], 'createPPA', {}, distribution='/ubuntutest',
-  ...     name='ppa')
+  ...     name='ppa'))
   HTTP/1.1 201 Created
   Status: 201
   ...
@@ -109,9 +110,9 @@
 
 But not for distributions that don't have PPAs enabled.
 
-  >>> print ppa_owner_webservice.named_post(
+  >>> print(ppa_owner_webservice.named_post(
   ...     ppa_owner['self_link'], 'createPPA', {}, distribution='/redhat',
-  ...     name='ppa')
+  ...     name='ppa'))
   HTTP/1.1 400 Bad Request
   Status: 400
   ...
@@ -124,8 +125,8 @@
 method, so they remain optional and default to Ubuntu and "ppa"
 respectively.
 
-  >>> print ppa_owner_webservice.named_post(
-  ...     ppa_owner['self_link'], 'createPPA', {})
+  >>> print(ppa_owner_webservice.named_post(
+  ...     ppa_owner['self_link'], 'createPPA', {}))
   HTTP/1.1 201 Created
   Status: 201
   ...

=== modified file 'lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt'
--- lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt	2018-05-04 21:59:32 +0000
+++ lib/lp/soyuz/stories/webservice/xx-source-package-publishing.txt	2018-07-01 09:01:37 +0000
@@ -38,7 +38,7 @@
     >>> def print_publications(pubs):
     ...     for display_name in sorted(
     ...         entry['display_name'] for entry in pubs['entries']):
-    ...         print display_name
+    ...         print(display_name)
 
     >>> print_publications(pubs)
     cdrkit 1.0 in breezy-autotest
@@ -172,7 +172,7 @@
     ...     distro_series=breezy['self_link'],
     ...     source_name="testwebservice").jsonBody()
 
-    >>> print pubs['entries'][0]['package_signer_link']
+    >>> print(pubs['entries'][0]['package_signer_link'])
     None
 
 Package deletion
@@ -184,7 +184,7 @@
     ...     cprov_archive['self_link'], 'getPublishedSources',
     ...     source_name="testwebservice", version="666",
     ...     exact_match=True).jsonBody()
-    >>> print pubs['total_size']
+    >>> print(pubs['total_size'])
     1
     >>> package = pubs['entries'][0]['self_link']
 
@@ -193,7 +193,7 @@
     >>> response = webservice.named_post(
     ...     package, 'requestDeletion',
     ...     removal_comment="No longer needed")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -202,7 +202,7 @@
     >>> response = cprov_webservice.named_post(
     ...     package, 'requestDeletion',
     ...     removal_comment="No longer needed")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -212,7 +212,7 @@
     ...     cprov_archive['self_link'], 'getPublishedSources',
     ...     source_name="testwebservice", version="666",
     ...     exact_match=True).jsonBody()
-    >>> print pubs['entries'][0]['removal_comment']
+    >>> print(pubs['entries'][0]['removal_comment'])
     No longer needed
 
 The package's binaries are also marked for deletion:
@@ -221,9 +221,9 @@
     >>> for bin in cprov_ppa.getAllPublishedBinaries(
     ...     name=u"testwebservice-bin"):
     ...     if bin.status != PackagePublishingStatus.DELETED:
-    ...         print "%s is not deleted when it should be" % bin.displayname
+    ...         print("%s is not deleted when it should be" % bin.displayname)
     ...     else:
-    ...         print "%s deleted OK." % bin.displayname
+    ...         print("%s deleted OK." % bin.displayname)
     testwebservice-bin 666 in breezy-autotest i386 deleted OK.
     testwebservice-bin 666 in breezy-autotest hppa deleted OK.
 
@@ -253,7 +253,7 @@
     ...     "/~cprov/+archive/ubuntu/p3a").jsonBody()
     >>> cprov_srcs_response_private = webservice.named_get(
     ...     cprov_private_ppa['self_link'], 'getPublishedSources')
-    >>> print cprov_srcs_response_private
+    >>> print(cprov_srcs_response_private)
     HTTP/1.1 200 Ok
     ...
 
@@ -261,7 +261,7 @@
 
     >>> response = user_webservice.named_get(
     ...     cprov_private_ppa['self_link'], 'getPublishedSources')
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -271,7 +271,7 @@
     >>> pubs = cprov_srcs_response_private.jsonBody()
     >>> private_publication_url = pubs['entries'][0]['self_link']
     >>> response = user_webservice.get(private_publication_url)
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -292,7 +292,7 @@
     >>> builds = webservice.named_get(
     ...     source_pub['self_link'], 'getBuilds').jsonBody()
     >>> for entry in sorted(builds['entries']):
-    ...     print entry['title']
+    ...     print(entry['title'])
     i386 build of pmount 0.1-1 in ubuntu warty RELEASE
 
 
@@ -310,7 +310,7 @@
     >>> builds = webservice.named_get(
     ...     source_pub['self_link'], 'getPublishedBinaries').jsonBody()
     >>> for entry in sorted(builds['entries']):
-    ...     print entry['display_name']
+    ...     print(entry['display_name'])
     pmount 0.1-1 in warty hppa
     pmount 0.1-1 in warty i386
 
@@ -351,8 +351,8 @@
     >>> def print_build_summaries(summaries):
     ...     for id, summary in summaries.items():
     ...         arch_tags = [build['arch_tag'] for build in summary['builds']]
-    ...         print "Source ID %s: %s (%s)" % (id, summary['status'],
-    ...                                          arch_tags)
+    ...         print("Source ID %s: %s (%s)" % (id, summary['status'],
+    ...                                          arch_tags))
 
 The results contain an entry for each source ID, with the summary status
 and a list of all the relevant builds for the summary:
@@ -374,7 +374,7 @@
     ...     entry['self_link'] for entry in pubs['entries']):
     ...     source_urls = webservice.named_get(
     ...         pub_link, 'sourceFileUrls').jsonBody()
-    ...     print source_urls
+    ...     print(source_urls)
     [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/cdrkit/1.0/foobar-1.0.dsc']
     [u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/firefox_0.9.2.orig.tar.gz',
      u'http://.../~cprov/+archive/ubuntu/ppa/+sourcefiles/iceweasel/1.0/iceweasel-1.0.dsc']
@@ -387,7 +387,7 @@
     ...     entry['self_link'] for entry in pubs['entries']):
     ...     binary_urls = webservice.named_get(
     ...         pub_link, 'binaryFileUrls').jsonBody()
-    ...     print binary_urls
+    ...     print(binary_urls)
     []
     [u'http://.../~cprov/+archive/ubuntu/ppa/+files/mozilla-firefox_0.9_i386.deb']
     []
@@ -403,8 +403,8 @@
     ...     spph = factory.makeSourcePackagePublishingHistory(
     ...         sourcepackagerelease=spr)
     ...     spph_url = canonical_url(spph, path_only_if_possible=True)
-    >>> print webservice.named_get(
-    ...     spph_url, 'changelogUrl', api_version='devel').jsonBody()
+    >>> print(webservice.named_get(
+    ...     spph_url, 'changelogUrl', api_version='devel').jsonBody())
     http://launchpad.dev/.../+sourcepub/.../+files/changelog
 
 The debdiff to a particular version can also be retrieved using the
@@ -444,7 +444,7 @@
 
 The URL is a standard proxied URL in case the file is private:
 
-    >>> print diff_url
+    >>> print(diff_url)
     http://launchpad.dev/~cprov/+archive/ubuntu/ppa/+files/...
 
 It will match the fake content we added earlier:
@@ -472,7 +472,7 @@
     >>> pubs = webservice.named_get(
     ...     cprov_archive_devel["self_link"], "getPublishedSources",
     ...     api_version="devel", source_name="testoverrides").jsonBody()
-    >>> print pubs["entries"][0]["section_name"]
+    >>> print(pubs["entries"][0]["section_name"])
     base
     >>> package = pubs["entries"][0]["self_link"]
 
@@ -481,7 +481,7 @@
     >>> response = webservice.named_post(
     ...     package, "changeOverride", api_version="devel",
     ...     new_section="admin")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
 
@@ -490,7 +490,7 @@
     >>> response = cprov_webservice.named_post(
     ...     package, "changeOverride", api_version="devel",
     ...     new_section="admin")
-    >>> print response
+    >>> print(response)
     HTTP/1.1 200 Ok
     ...
 
@@ -499,5 +499,5 @@
     >>> pubs = webservice.named_get(
     ...     cprov_archive["self_link"], "getPublishedSources",
     ...     source_name="testoverrides").jsonBody()
-    >>> print pubs["entries"][0]["section_name"]
+    >>> print(pubs["entries"][0]["section_name"])
     admin

=== modified file 'lib/lp/soyuz/tests/test_doc.py'
--- lib/lp/soyuz/tests/test_doc.py	2018-05-27 18:32:33 +0000
+++ lib/lp/soyuz/tests/test_doc.py	2018-07-01 09:01:37 +0000
@@ -21,7 +21,10 @@
     LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
     )
-from lp.testing.pages import PageTestSuite
+from lp.testing.pages import (
+    PageTestSuite,
+    setUpGlobs,
+    )
 from lp.testing.systemdocs import (
     LayeredDocFileSuite,
     setUp,
@@ -165,13 +168,15 @@
     suite = unittest.TestSuite()
 
     stories_dir = os.path.join(os.path.pardir, 'stories')
-    suite.addTest(PageTestSuite(stories_dir))
+    suite.addTest(PageTestSuite(
+        stories_dir, setUp=lambda test: setUpGlobs(test, future=True)))
     stories_path = os.path.join(here, stories_dir)
     for story_entry in scandir.scandir(stories_path):
         if not story_entry.is_dir():
             continue
         story_path = os.path.join(stories_dir, story_entry.name)
-        suite.addTest(PageTestSuite(story_path))
+        suite.addTest(PageTestSuite(
+            story_path, setUp=lambda test: setUpGlobs(test, future=True)))
 
     # Add special needs tests
     for key in sorted(special):


References