← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/bugs-doctests-future-imports into lp:launchpad

 

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

Commit message:
Convert doctests under lp.bugs to Launchpad's preferred __future__ imports.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/bugs-doctests-future-imports/+merge/348785
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/bugs-doctests-future-imports into lp:launchpad.
=== modified file 'lib/lp/bugs/doc/bug-change.txt'
--- lib/lp/bugs/doc/bug-change.txt	2017-05-31 17:31:58 +0000
+++ lib/lp/bugs/doc/bug-change.txt	2018-06-30 11:02:51 +0000
@@ -31,10 +31,10 @@
 
 But the basic attributes are still available.
 
-    >>> print base_instance.when
+    >>> print(base_instance.when)
     2009-03-13 10:09:00+00:00
 
-    >>> print base_instance.person.displayname
+    >>> print(base_instance.person.displayname)
     Ford Prefect
 
 Because the base class is abstract, you can't pass it to
@@ -86,10 +86,10 @@
     >>> def print_bug_activity(activity):
     ...     for activity in activity:
     ...         if activity not in activity_to_ignore:
-    ...             print "%s: %s %s => %s (%s)" % (
+    ...             print("%s: %s %s => %s (%s)" % (
     ...                 activity.datechanged, activity.whatchanged,
     ...                 activity.oldvalue, activity.newvalue,
-    ...                 activity.person.displayname)
+    ...                 activity.person.displayname))
 
 Creating bugs generates activity records, indirectly, using the
 addChange() API, but we want to ignore them for now.
@@ -108,14 +108,14 @@
 
     >>> from lp.bugs.model.bugnotification import BugNotification
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     Some message text
 
 The notification's recipients are taken from the recipients parameter
 passed to addChange().
 
     >>> for recipient in latest_notification.recipients:
-    ...     print recipient.person.displayname
+    ...     print(recipient.person.displayname)
     Zaphod Beeblebrox
 
 But if getBugActivity() returns None, no activity entries will be added.
@@ -159,14 +159,14 @@
     >>> example_bug.addChange(
     ...     TestBugChange(when=nowish, person=example_person))
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     Some message text
 
     >>> recipients = [
     ...     recipient.person.displayname
     ...     for recipient in latest_notification.recipients]
     >>> for name in sorted(recipients):
-    ...     print name
+    ...     print(name)
     Ford Prefect
     Meta-data subscriber
 
@@ -234,10 +234,10 @@
 method.
 
     >>> activity_data = simple_change.getBugActivity()
-    >>> print pretty(activity_data)
-    {'newvalue': 'Spam',
+    >>> print(pretty(activity_data))
+    {'newvalue': u'Spam',
      'oldvalue': u'Reality is on the blink again',
-     'whatchanged': 'title'}
+     'whatchanged': u'title'}
 
 
 === BugDescriptionChange ===
@@ -253,7 +253,7 @@
     ...     when=nowish, person=example_person,
     ...     what_changed='description', old_value=example_bug.description,
     ...     new_value='Well, maybe not')
-    >>> print bug_desc_change.getBugNotification()['text']
+    >>> print(bug_desc_change.getBugNotification()['text'])
     ** Description changed:
     <BLANKLINE>
     - I'm tired of thinking up funny strings for tests
@@ -273,7 +273,7 @@
     ...     when=nowish, person=example_person,
     ...     what_changed='title', old_value=example_bug.title,
     ...     new_value='Spam')
-    >>> print bug_title_change.getBugNotification()['text']
+    >>> print(bug_title_change.getBugNotification()['text'])
     ** Summary changed:
     <BLANKLINE>
     - Reality is on the blink again
@@ -283,7 +283,7 @@
 'summary' rather than 'title'. This is to maintain naming consistency
 within the UI.
 
-    >>> print bug_title_change.getBugActivity()['whatchanged']
+    >>> print(bug_title_change.getBugActivity()['whatchanged'])
     summary
 
 
@@ -300,14 +300,14 @@
     ...     when=nowish, person=example_person,
     ...     what_changed='duplicateof', old_value=None,
     ...     new_value=duplicate_bug)
-    >>> print bug_duplicate_change.getBugNotification()['text']
+    >>> print(bug_duplicate_change.getBugNotification()['text'])
     ** This bug has been marked a duplicate of bug ...
        Fish can't walk
 
 BugDuplicateChange overrides getBugActivity() to customize all the
 returned fields.
 
-    >>> print pretty(bug_duplicate_change.getBugActivity())
+    >>> print(pretty(bug_duplicate_change.getBugActivity()))
     {'newvalue': '...',
      'whatchanged': 'marked as duplicate'}
 
@@ -329,14 +329,14 @@
 other attribute change. The list of tags is converted to a
 space-separated string for display.
 
-    >>> print pretty(tags_change.getBugActivity())
+    >>> print(pretty(tags_change.getBugActivity()))
     {'newvalue': u'second-tag third-tag zillionth-tag',
      'oldvalue': u'first-tag second-tag third-tag',
-     'whatchanged': 'tags'}
+     'whatchanged': u'tags'}
 
 Addtions and removals are expressed separately in the notification.
 
-    >>> print tags_change.getBugNotification()['text']
+    >>> print(tags_change.getBugNotification()['text'])
     ** Tags removed: first-tag
     ** Tags added: zillionth-tag
 
@@ -357,11 +357,11 @@
     >>> bug_cve_linked = CveLinkedToBug(
     ...     when=nowish, person=example_person, cve=cve)
 
-    >>> print pretty(bug_cve_linked.getBugActivity())
+    >>> print(pretty(bug_cve_linked.getBugActivity()))
     {'newvalue': u'1999-8979',
      'whatchanged': 'cve linked'}
 
-    >>> print bug_cve_linked.getBugNotification()['text']
+    >>> print(bug_cve_linked.getBugNotification()['text'])
     ** CVE added: https://cve.mitre.org/cgi-bin/cvename.cgi?name=1999-8979
 
 And when a CVE is unlinked from a bug.
@@ -369,11 +369,11 @@
     >>> bug_cve_unlinked = CveUnlinkedFromBug(
     ...     when=nowish, person=example_person, cve=cve)
 
-    >>> print pretty(bug_cve_unlinked.getBugActivity())
+    >>> print(pretty(bug_cve_unlinked.getBugActivity()))
     {'oldvalue': u'1999-8979',
      'whatchanged': 'cve unlinked'}
 
-    >>> print bug_cve_unlinked.getBugNotification()['text']
+    >>> print(bug_cve_unlinked.getBugNotification()['text'])
     ** CVE removed: https://cve.mitre.org/cgi-bin/cvename.cgi?name=1999-8979
 
 
@@ -394,13 +394,13 @@
     ...     what_changed='security_related',
     ...     old_value=None, new_value=attachment)
 
-    >>> print pretty(attachment_change.getBugActivity())
+    >>> print(pretty(attachment_change.getBugActivity()))
     {'newvalue':
          u'sample-attachment http://bugs.launchpad.dev/bugs/...+files/...',
      'oldvalue': None,
      'whatchanged': 'attachment added'}
 
-    >>> print attachment_change.getBugNotification()['text']
+    >>> print(attachment_change.getBugNotification()['text'])
     ** Attachment added: "sample-attachment"
     http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/...
 
@@ -411,13 +411,13 @@
     ...     what_changed='security_related',
     ...     old_value=attachment, new_value=None)
 
-    >>> print pretty(attachment_change.getBugActivity())
+    >>> print(pretty(attachment_change.getBugActivity()))
     {'newvalue': None,
      'oldvalue':
          u'sample-attachment http://bugs.launchpad.dev/bugs/...+files/...',
      'whatchanged': 'attachment removed'}
 
-    >>> print attachment_change.getBugNotification()['text']
+    >>> print(attachment_change.getBugNotification()['text'])
     ** Attachment removed: "sample-attachment"
     http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/...
 
@@ -450,13 +450,13 @@
     ...     new_value=BugTaskStatus.FIXRELEASED,
     ...     bug_task=example_bug_task)
 
-    >>> print task_attribute_change.display_activity_label
+    >>> print(task_attribute_change.display_activity_label)
     status
-    >>> print task_attribute_change.display_notification_label
+    >>> print(task_attribute_change.display_notification_label)
     Status
-    >>> print task_attribute_change.display_old_value
+    >>> print(task_attribute_change.display_old_value)
     New
-    >>> print task_attribute_change.display_new_value
+    >>> print(task_attribute_change.display_new_value)
     Fix Released
 
 Several types of attribute change can be handled by
@@ -475,13 +475,13 @@
     ...     bug_task=example_bug_task, when=nowish, person=example_person,
     ...     what_changed='status', old_value=BugTaskStatus.NEW,
     ...     new_value=BugTaskStatus.FIXRELEASED)
-    >>> print pretty(status_change.getBugActivity())
+    >>> print(pretty(status_change.getBugActivity()))
     {'newvalue': 'Fix Released',
      'oldvalue': 'New',
      'whatchanged': u'heart-of-gold: status'}
 
     >>> notification_text = status_change.getBugNotification()['text']
-    >>> print notification_text #doctest: -NORMALIZE_WHITESPACE
+    >>> print(notification_text) #doctest: -NORMALIZE_WHITESPACE
     ** Changed in: heart-of-gold
            Status: New => Fix Released
 
@@ -499,13 +499,13 @@
     ...     what_changed='importance',
     ...     old_value=BugTaskImportance.UNDECIDED,
     ...     new_value=BugTaskImportance.CRITICAL)
-    >>> print pretty(importance_change.getBugActivity())
+    >>> print(pretty(importance_change.getBugActivity()))
     {'newvalue': 'Critical',
      'oldvalue': 'Undecided',
      'whatchanged': u'heart-of-gold: importance'}
 
     >>> notification_text = importance_change.getBugNotification()['text']
-    >>> print notification_text #doctest: -NORMALIZE_WHITESPACE
+    >>> print(notification_text) #doctest: -NORMALIZE_WHITESPACE
     ** Changed in: heart-of-gold
        Importance: Undecided => Critical
 
@@ -526,13 +526,13 @@
     ...     bug_task=example_bug_task, when=nowish,
     ...     person=example_person, what_changed='milestone',
     ...     old_value=None, new_value=milestone)
-    >>> print pretty(milestone_change.getBugActivity())
+    >>> print(pretty(milestone_change.getBugActivity()))
     {'newvalue': u'example-milestone',
      'oldvalue': None,
      'whatchanged': u'heart-of-gold: milestone'}
 
     >>> notification_text = milestone_change.getBugNotification()['text']
-    >>> print notification_text #doctest: -NORMALIZE_WHITESPACE
+    >>> print(notification_text) #doctest: -NORMALIZE_WHITESPACE
     ** Changed in: heart-of-gold
         Milestone: None => example-milestone
 
@@ -555,13 +555,13 @@
     ...     bug_task=example_bug_task, when=nowish,
     ...     person=example_person, what_changed='bugwatch',
     ...     old_value=None, new_value=bug_watch)
-    >>> print pretty(bug_watch_change.getBugActivity())
+    >>> print(pretty(bug_watch_change.getBugActivity()))
     {'newvalue': u'bugs.example.com/ #1245',
      'oldvalue': None,
      'whatchanged': u'heart-of-gold: remote watch'}
 
     >>> notification_text = bug_watch_change.getBugNotification()['text']
-    >>> print notification_text #doctest: -NORMALIZE_WHITESPACE
+    >>> print(notification_text) #doctest: -NORMALIZE_WHITESPACE
     ** Changed in: heart-of-gold
      Remote watch: None => bugs.example.com/ #1245
 
@@ -578,13 +578,13 @@
     ...     bug_task=example_bug_task, when=nowish,
     ...     person=example_person, what_changed='assignee',
     ...     old_value=None, new_value=example_person)
-    >>> print pretty(assignee_change.getBugActivity())
+    >>> print(pretty(assignee_change.getBugActivity()))
     {'newvalue': u'Ford Prefect (ford-prefect)',
      'oldvalue': None,
      'whatchanged': u'heart-of-gold: assignee'}
 
     >>> notification_text = assignee_change.getBugNotification()['text']
-    >>> print notification_text #doctest: -NORMALIZE_WHITESPACE
+    >>> print(notification_text) #doctest: -NORMALIZE_WHITESPACE
     ** Changed in: heart-of-gold
          Assignee: (unassigned) => Ford Prefect (ford-prefect)
 
@@ -605,11 +605,11 @@
     ...     what_changed='target',
     ...     old_value=example_bug_task.target,
     ...     new_value=new_target)
-    >>> print pretty(target_change.getBugActivity())
+    >>> print(pretty(target_change.getBugActivity()))
     {'newvalue': u'magrathea',
      'oldvalue': u'heart-of-gold',
      'whatchanged': 'affects'}
 
     >>> notification_text = target_change.getBugNotification()['text']
-    >>> print notification_text #doctest: -NORMALIZE_WHITESPACE
+    >>> print(notification_text) #doctest: -NORMALIZE_WHITESPACE
     ** Project changed: heart-of-gold => magrathea

=== modified file 'lib/lp/bugs/doc/bug-reported-acknowledgement.txt'
--- lib/lp/bugs/doc/bug-reported-acknowledgement.txt	2011-05-05 05:46:41 +0000
+++ lib/lp/bugs/doc/bug-reported-acknowledgement.txt	2018-06-30 11:02:51 +0000
@@ -34,17 +34,17 @@
 DistroSeries and SourcePackages defer to the Distribution:
 
     >>> distro_series = distribution.getSeries('warty')
-    >>> print distro_series.bug_reported_acknowledgement
+    >>> print(distro_series.bug_reported_acknowledgement)
     Bug reported on Ubuntu.
 
     >>> source_package = distro_series.getSourcePackage('alsa-utils')
-    >>> print source_package.bug_reported_acknowledgement
+    >>> print(source_package.bug_reported_acknowledgement)
     Bug reported on Ubuntu.
 
 ProductSeries defer to the Product:
 
     >>> product_series = product.getSeries('trunk')
-    >>> print product_series.bug_reported_acknowledgement
+    >>> print(product_series.bug_reported_acknowledgement)
     Bug reported on Mozilla Firefox.
 
 One day these objects that defer bug_reported_acknowledgement may have
@@ -81,7 +81,7 @@
     ...         "%s let %s have access." % (
     ...             context.displayname,
     ...             getUtility(ILaunchBag).user.displayname))
-    ...     print context.bug_reported_acknowledgement
+    ...     print(context.bug_reported_acknowledgement)
 
     >>> check_access("no-priv@xxxxxxxxxxxxx", distribution)
     Traceback (most recent call last):

=== modified file 'lib/lp/bugs/doc/bug-reporting-guidelines.txt'
--- lib/lp/bugs/doc/bug-reporting-guidelines.txt	2011-05-05 05:46:41 +0000
+++ lib/lp/bugs/doc/bug-reporting-guidelines.txt	2018-06-30 11:02:51 +0000
@@ -33,17 +33,17 @@
 DistroSeries and SourcePackages defer to the Distribution:
 
     >>> distro_series = distribution.getSeries('warty')
-    >>> print distro_series.bug_reporting_guidelines
+    >>> print(distro_series.bug_reporting_guidelines)
     Welcome to Ubuntu!
 
     >>> source_package = distro_series.getSourcePackage('alsa-utils')
-    >>> print source_package.bug_reporting_guidelines
+    >>> print(source_package.bug_reporting_guidelines)
     Welcome to Ubuntu!
 
 ProductSeries defer to the Product:
 
     >>> product_series = product.getSeries('trunk')
-    >>> print product_series.bug_reporting_guidelines
+    >>> print(product_series.bug_reporting_guidelines)
     Welcome to Mozilla Firefox!
 
 One day these objects that defer bug_reporting_guidelines may have
@@ -80,7 +80,7 @@
     ...         "%s let %s have access." % (
     ...             context.displayname,
     ...             getUtility(ILaunchBag).user.displayname))
-    ...     print context.bug_reporting_guidelines
+    ...     print(context.bug_reporting_guidelines)
 
     >>> check_access("no-priv@xxxxxxxxxxxxx", distribution)
     Traceback (most recent call last):

=== modified file 'lib/lp/bugs/doc/bug-tags.txt'
--- lib/lp/bugs/doc/bug-tags.txt	2012-12-10 13:43:47 +0000
+++ lib/lp/bugs/doc/bug-tags.txt	2018-06-30 11:02:51 +0000
@@ -25,7 +25,7 @@
     >>> from lp.bugs.model.bug import BugTag
     >>> bugtags = BugTag.selectBy(bugID=bug_one.id, orderBy='tag')
     >>> for bugtag in bugtags:
-    ...     print bugtag.tag
+    ...     print(bugtag.tag)
     sco
     svg
 
@@ -38,7 +38,7 @@
 
     >>> bugtags = BugTag.selectBy(bugID=bug_one.id, orderBy='tag')
     >>> for bugtag in bugtags:
-    ...     print bugtag.tag
+    ...     print(bugtag.tag)
     installl
     sco
     svg
@@ -58,7 +58,7 @@
 
     >>> bugtags = BugTag.selectBy(bugID=bug_one.id, orderBy='tag')
     >>> for bugtag in bugtags:
-    ...     print bugtag.tag
+    ...     print(bugtag.tag)
     install
     sco
 
@@ -119,7 +119,7 @@
     ...
     WidgetInputError...
 
-    >>> print tags_widget._error.doc()
+    >>> print(tags_widget._error.doc())
     &#x27;!!!!&#x27; isn&#x27;t a valid tag name. Tags must start with a
     letter or number and be lowercase. The characters &quot;+&quot;,
     &quot;-&quot; and &quot;.&quot; are also allowed after the first
@@ -179,9 +179,9 @@
 _tagsFromFieldValue() converts tags from the field value to tags for
 display. The absense of tags causes it to return None:
 
-    >>> print tags_frozen_set_widget._tagsFromFieldValue(None)
+    >>> print(tags_frozen_set_widget._tagsFromFieldValue(None))
     None
-    >>> print tags_frozen_set_widget._tagsFromFieldValue(frozenset())
+    >>> print(tags_frozen_set_widget._tagsFromFieldValue(frozenset()))
     None
 
 Tags are ordered before returning:
@@ -210,7 +210,7 @@
 
 A regular BugTagsWidget is rendered as an <input> tag,
 
-    >>> print tags_widget()
+    >>> print(tags_widget())
     <input...type="text"...
 
 A LargeBugTagsWidget is rendered as a <textarea>,
@@ -218,7 +218,7 @@
     >>> from lp.bugs.browser.widgets.bug import LargeBugTagsWidget
     >>> large_text_widget = LargeBugTagsWidget(
     ...     bug_tags_field, tag_field, request)
-    >>> print large_text_widget()
+    >>> print(large_text_widget())
     <textarea...
 
 
@@ -234,7 +234,7 @@
     >>> svg_tasks = ubuntu.searchTasks(
     ...     BugTaskSearchParams(tag=all(u'svg'), user=None))
     >>> for bugtask in svg_tasks:
-    ...     print bugtask.bug.id, bugtask.bug.tags
+    ...     print(bugtask.bug.id, bugtask.bug.tags)
     1 [u'sco', u'svg']
 
 We can also search for bugs with any of the tags in a supplied list.
@@ -243,7 +243,7 @@
     >>> crash_dataloss_tasks = ubuntu.searchTasks(BugTaskSearchParams(
     ...     tag=any(u'crash', u'dataloss'), orderby='id', user=None))
     >>> for bugtask in crash_dataloss_tasks:
-    ...     print bugtask.bug.id, bugtask.bug.tags
+    ...     print(bugtask.bug.id, bugtask.bug.tags)
     2 [u'dataloss', u'pebcak']
     9 [u'crash']
     10 [u'crash']
@@ -255,7 +255,7 @@
     >>> crash_burn_tasks = ubuntu.searchTasks(BugTaskSearchParams(
     ...     tag=all(u'crash', u'burn'), orderby='id', user=None))
     >>> for bugtask in crash_burn_tasks:
-    ...     print bugtask.bug.id, bugtask.bug.tags
+    ...     print(bugtask.bug.id, bugtask.bug.tags)
     10 [u'burn', u'crash']
     >>> getUtility(IBugSet).get(10).tags = [u'crash']
 
@@ -280,7 +280,7 @@
     XXX some_tag_tasks = ubuntu.searchTasks(
     ...     BugTaskSearchParams(searchtext=u'some-tag', user=None))
     XXX for bugtask in some_tag_tasks:
-    ...     print bugtask.id, bugtask.bug.id, bugtask.bug.tags
+    ...     print(bugtask.id, bugtask.bug.id, bugtask.bug.tags)
     1 [u'some-tag']
 
 

=== modified file 'lib/lp/bugs/doc/bug-watch-activity.txt'
--- lib/lp/bugs/doc/bug-watch-activity.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/bugs/doc/bug-watch-activity.txt	2018-06-30 11:02:51 +0000
@@ -21,7 +21,7 @@
 
 When a BugWatch is first created there has been no activity on it.
 
-    >>> print bug_watch.activity.count()
+    >>> print(bug_watch.activity.count())
     0
 
     >>> bug_watch.addActivity()
@@ -31,7 +31,7 @@
 We can access the BugWatchActivity record by looking at the BugWatch's
 activity property.
 
-    >>> print bug_watch.activity.count()
+    >>> print(bug_watch.activity.count())
     1
 
     >>> activity = bug_watch.activity.first()
@@ -46,15 +46,15 @@
 
 The BugWatchActivity's result will be BugWatchActivityStatus.SYNC_SUCCEEDED.
 
-    >>> print activity.result.title
+    >>> print(activity.result.title)
     Synchronisation succeeded
 
 The other fields on the BugWatchActivity record, which aren't required,
 will all be None.
 
-    >>> print activity.message
+    >>> print(activity.message)
     None
-    >>> print activity.oops_id
+    >>> print(activity.oops_id)
     None
 
 
@@ -77,7 +77,7 @@
 An extra activity item will have been added to the BugWatch's activity
 property.
 
-    >>> print bug_watch.activity.count()
+    >>> print(bug_watch.activity.count())
     2
 
 The most recent activity entry will have a result of
@@ -85,17 +85,17 @@
 successful.
 
     >>> most_recent_activity = bug_watch.activity.first()
-    >>> print most_recent_activity.result.title
+    >>> print(most_recent_activity.result.title)
     Synchronisation succeeded
 
 Its message will also be empty
 
-    >>> print most_recent_activity.message
+    >>> print(most_recent_activity.message)
     None
 
 As will its oops_id
 
-    >>> print most_recent_activity.oops_id
+    >>> print(most_recent_activity.oops_id)
     None
 
 If the remote bug tracker breaks during an update the error will be
@@ -111,19 +111,19 @@
 
 Another entry will have been added to the watch's activity property.
 
-    >>> print bug_watch.activity.count()
+    >>> print(bug_watch.activity.count())
     3
 
 And this time its result field will record that the remote bug was
 not found.
 
     >>> most_recent_activity = bug_watch.activity.first()
-    >>> print most_recent_activity.result.title
+    >>> print(most_recent_activity.result.title)
     Unparsable Bug
 
 The OOPS ID for the error will also have been recorded.
 
-    >>> print most_recent_activity.oops_id
+    >>> print(most_recent_activity.oops_id)
     OOPS...
 
 The CheckwatchesMaster also adds BugWatchActivity entries when errors occur
@@ -133,10 +133,10 @@
     >>> updater.updateBugWatches(broken_bugtracker, [bug_watch])
     >>> most_recent_activity = bug_watch.activity.first()
 
-    >>> print most_recent_activity.result.title
+    >>> print(most_recent_activity.result.title)
     Unknown
 
 The OOPS ID of the error is recorded so that we can debug it.
 
-    >>> print most_recent_activity.oops_id
+    >>> print(most_recent_activity.oops_id)
     OOPS...

=== modified file 'lib/lp/bugs/doc/bug.txt'
--- lib/lp/bugs/doc/bug.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bug.txt	2018-06-30 11:02:51 +0000
@@ -17,13 +17,13 @@
 To retrieve a specific Bug, use IBugSet.get:
 
     >>> firefox_crashes = bugset.get(6)
-    >>> print firefox_crashes.title
+    >>> print(firefox_crashes.title)
     Firefox crashes when Save As dialog for a nonexistent window is closed
 
 Or you can use IBugSet.getByNameOrID to get it by its nickname:
 
     >>> blackhole_bug = bugset.getByNameOrID('blackhole')
-    >>> print blackhole_bug.title
+    >>> print(blackhole_bug.title)
     Blackhole Trash folder
 
 If the bug can't be found, a zope.exceptions.NotFoundError will be
@@ -37,7 +37,7 @@
     >>> bugset.getByNameOrID('+bugs')
     Traceback (most recent call last):
       ...
-    NotFoundError: 'Unable to locate bug with nickname +bugs.'
+    NotFoundError: u'Unable to locate bug with nickname +bugs.'
 
 It is also possible to retrieve a number of bugs by specifying the bug numbers
 of interest.
@@ -46,28 +46,28 @@
 result set below has only one element.
 
     >>> result_set = bugset.getByNumbers([6, 1234])
-    >>> print result_set.count()
+    >>> print(result_set.count())
     1
 
     >>> [the_bug_found] = result_set
-    >>> print the_bug_found.title
+    >>> print(the_bug_found.title)
     Firefox crashes when Save As dialog for a nonexistent window is closed
 
     >>> result_set = bugset.getByNumbers([6, 1])
-    >>> print result_set.count()
+    >>> print(result_set.count())
     2
 
-    >>> print [(bug.id, bug.title[:40]) for bug in result_set]
+    >>> print([(bug.id, bug.title[:40]) for bug in result_set])
     [(1, u'Firefox does not support SVG'),
      (6, u'Firefox crashes when Save As dialog for ')]
 
 If no bug numbers are specified an empty result set is returned.
 
     >>> result_set = bugset.getByNumbers(None)
-    >>> print result_set.count()
+    >>> print(result_set.count())
     0
     >>> result_set = bugset.getByNumbers([])
-    >>> print result_set.count()
+    >>> print(result_set.count())
     0
 
 Bug creation events
@@ -80,9 +80,9 @@
     >>> from lazr.lifecycle.event import IObjectCreatedEvent
 
     >>> def show_bug_creation_event(bug, event):
-    ...     print "New bug created: %s" % (bug.title,)
-    ...     print "          Owner: %s" % (bug.owner.name,)
-    ...     print "       Filed by: %s" % (event.user.name,)
+    ...     print("New bug created: %s" % (bug.title,))
+    ...     print("          Owner: %s" % (bug.owner.name,))
+    ...     print("       Filed by: %s" % (event.user.name,))
 
     >>> globalSiteManager.registerHandler(
     ...     show_bug_creation_event, (IBug, IObjectCreatedEvent))
@@ -150,7 +150,7 @@
 
     >>> from lp.services.webapp import canonical_url
     >>> login(ANONYMOUS)
-    >>> print canonical_url(firefox_crashes)
+    >>> print(canonical_url(firefox_crashes))
     http://.../bugs/6
 
 
@@ -189,7 +189,7 @@
 
 There are currently no people subscribed to this bug:
 
-    >>> print firefox_crashes.subscriptions.count()
+    >>> print(firefox_crashes.subscriptions.count())
     0
 
 The rule with private bugs is that only direct subscribers can view the
@@ -391,21 +391,21 @@
 So, let's continue:
 
     >>> for subscription in public_bug.subscriptions:
-    ...     print subscription.person.name
+    ...     print(subscription.person.name)
     name16
 
 The first comment made (this is submitted in the bug report) is set to
 the description of the bug:
 
-    >>> print public_bug.description
+    >>> print(public_bug.description)
     blah blah blah
 
 The bug description can also be accessed through the task:
 
-    >>> print public_bug.bugtasks[0].bug.description
+    >>> print(public_bug.bugtasks[0].bug.description)
     blah blah blah
     >>> public_bug.description = 'a new description'
-    >>> print public_bug.bugtasks[0].bug.description
+    >>> print(public_bug.bugtasks[0].bug.description)
     a new description
 
 When a private bug is filed:
@@ -534,12 +534,12 @@
 
     >>> firefox_task = firefox_bug.bugtasks[0]
 
-    >>> print firefox_task.bugtargetdisplayname
+    >>> print(firefox_task.bugtargetdisplayname)
     Mozilla Firefox
 
-    >>> print firefox_task.importance.title
+    >>> print(firefox_task.importance.title)
     Undecided
-    >>> print firefox_task.status.title
+    >>> print(firefox_task.status.title)
     New
 
     >>> bugtask_before_modification = Snapshot(
@@ -567,7 +567,7 @@
 
     >>> thunderbird = productset.getByName("thunderbird")
 
-    >>> print thunderbird.name
+    >>> print(thunderbird.name)
     thunderbird
 
     >>> thunderbird_task = getUtility(IBugTaskSet).createTask(
@@ -603,7 +603,7 @@
     ...     owner=foobar, target=ubuntu)
     >>> notify(ObjectCreatedEvent(ubuntu_task))
 
-    >>> print ubuntu_task.distribution.title
+    >>> print(ubuntu_task.distribution.title)
     Ubuntu
 
 And for a specific distribution series.
@@ -613,7 +613,7 @@
     ...     owner=foobar, target=warty)
     >>> notify(ObjectCreatedEvent(warty_task))
 
-    >>> print warty_task.distroseries.title
+    >>> print(warty_task.distroseries.title)
     The Warty Warthog Release
 
 Also for a specific distribution source package.
@@ -624,7 +624,7 @@
     ...     owner=foobar, target=linux_source)
     >>> notify(ObjectCreatedEvent(linux_task))
 
-    >>> print linux_task.bugtargetname
+    >>> print(linux_task.bugtargetname)
     linux-source-2.6.15 (Tubuntu)
 
 And for a distro series source package.
@@ -638,20 +638,20 @@
     ...     owner=foobar, target=warty_fox_package)
     >>> notify(ObjectCreatedEvent(warty_fox_task))
 
-    >>> print warty_fox_task.bugtargetname
+    >>> print(warty_fox_task.bugtargetname)
     mozilla-firefox (Ubuntu Warty)
 
-    >>> print warty_fox_task.distroseries.name
+    >>> print(warty_fox_task.distroseries.name)
     warty
 
-    >>> print warty_fox_task.sourcepackagename.name
+    >>> print(warty_fox_task.sourcepackagename.name)
     mozilla-firefox
 
 The first task is available as default_bugtask. Launchpad often views
 bugs in the context of a bugtask, and the default choice is the first
 or oldest bugtask.
 
-    >>> print firefox_bug.default_bugtask.bugtargetdisplayname
+    >>> print(firefox_bug.default_bugtask.bugtargetdisplayname)
     Mozilla Firefox
 
 Changing bug visibility.
@@ -696,7 +696,7 @@
 
     >>> bug_before_modification = Snapshot(firefox_bug, providing=IBug)
 
-    >>> print firefox_bug.duplicateof
+    >>> print(firefox_bug.duplicateof)
     None
     >>> firefox_bug.markAsDuplicate(factory.makeBug())
 
@@ -812,7 +812,7 @@
     >>> bugwatch_before_modification = Snapshot(
     ...     bugwatch, providing=IBugWatch)
 
-    >>> print bugwatch.remotebug
+    >>> print(bugwatch.remotebug)
     1234
 
     >>> bugwatch.remotebug = '5678'
@@ -839,7 +839,7 @@
     ...     )
 
     >>> imported_message = bugwatch.getImportedBugMessages()[0]
-    >>> print imported_message.message.text_contents
+    >>> print(imported_message.message.text_contents)
     blah blah blah blah remotely
 
 Subscribing and unsubscribing does *not* trigger an update of
@@ -889,7 +889,7 @@
 incomplete, and show the status of each of the tasts:
 
     >>> for task in b8.bugtasks:
-    ...     print task.bugtargetdisplayname, task.is_complete
+    ...     print(task.bugtargetdisplayname, task.is_complete)
     Mozilla Firefox False
     mozilla-firefox (Debian) True
 
@@ -904,7 +904,7 @@
 You can get the set of bugtasks for at bug with the 'bugtasks' attribute:
 
     >>> bug_two = bugset.get(2)
-    >>> for task in bug_two.bugtasks: print task.target.displayname
+    >>> for task in bug_two.bugtasks: print(task.target.displayname)
     Tomcat
     Ubuntu
     Hoary
@@ -914,7 +914,7 @@
 You can also get a list of the "LP pillars" affected by a particular bug.
 
     >>> for pillar in bug_two.affected_pillars:
-    ...     print pillar.displayname
+    ...     print(pillar.displayname)
     Tomcat
     Ubuntu
     Debian
@@ -1087,9 +1087,9 @@
 exported using the webservice API.
 
     >>> for indexed_message in bug_two.indexed_messages:
-    ...     print '%s\t%s\t%s' % (
+    ...     print('%s\t%s\t%s' % (
     ...         indexed_message.index, indexed_message.subject,
-    ...         indexed_message.inside.title)
+    ...         indexed_message.inside.title))
     0 PEBCAK
     Bug #2 in Tomcat: "Blackhole Trash folder"
     1 Fantastic idea, I'd really like to see this
@@ -1114,13 +1114,13 @@
 including the anonymous user, are neither marked as affected nor as
 unaffected.
 
-    >>> print test_bug.isUserAffected(test_bug.owner)
+    >>> print(test_bug.isUserAffected(test_bug.owner))
     True
 
-    >>> print test_bug.isUserAffected(affected_user)
+    >>> print(test_bug.isUserAffected(affected_user))
     None
 
-    >>> print test_bug.isUserAffected(None)
+    >>> print(test_bug.isUserAffected(None))
     None
 
 When we mark a bug as affecting a new user, the affected_users_count
@@ -1141,7 +1141,7 @@
 
 We can mark a user as unaffected by a bug.
 
-    >>> print test_bug.isUserAffected(unaffected_user)
+    >>> print(test_bug.isUserAffected(unaffected_user))
     None
     >>> test_bug.markUserAffected(unaffected_user, affected=False)
     >>> test_bug.isUserAffected(unaffected_user)
@@ -1168,19 +1168,19 @@
 
 We can also get the collection of users affected by a bug.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in test_bug.users_affected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in test_bug.users_affected)))
     bruce-dickinson
     paul-dianno
 
     >>> unaffecting_bug = factory.makeBug()
-    >>> print list(unaffecting_bug.users_affected)
+    >>> print(list(unaffecting_bug.users_affected))
     [<Person at ...>]
 
 Similarly, we can get the collection of users unaffected by a bug.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in test_bug.users_unaffected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in test_bug.users_unaffected)))
     blaze-bayley
 
 If a user is marked as being affected by a bug (either by explicitly
@@ -1196,8 +1196,8 @@
 
 And the list of users the master bug affects includes that user.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in test_bug.users_affected_with_dupes))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in test_bug.users_affected_with_dupes)))
     bruce-dickinson
     paul-dianno
     sheila-shakespeare
@@ -1213,8 +1213,8 @@
 And the list of users that the master bug affects still includes the
 user, of course.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in test_bug.users_affected_with_dupes))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in test_bug.users_affected_with_dupes)))
     bruce-dickinson
     paul-dianno
     sheila-shakespeare
@@ -1240,18 +1240,18 @@
 
 Both duplicates claim to affect just that user:
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_one.users_affected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_one.users_affected)))
     sheila-shakespeare
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_two.users_affected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_two.users_affected)))
     sheila-shakespeare
 
 And the list of users that the master bug affects includes the user
 exactly once, of course.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in test_bug.users_affected_with_dupes))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in test_bug.users_affected_with_dupes)))
     bruce-dickinson
     napoleon-bonaparte
     paul-dianno
@@ -1277,26 +1277,26 @@
 The dup user no longer appears as affected by the master bug nor
 either of the dups:
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in test_bug.users_affected_with_dupes))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in test_bug.users_affected_with_dupes)))
     bruce-dickinson
     napoleon-bonaparte
     paul-dianno
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_one.users_affected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_one.users_affected)))
     <BLANKLINE>
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_two.users_affected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_two.users_affected)))
     <BLANKLINE>
 
 Since the user who filed the first two dups had an entry explicitly
 saying they were affected, they now claim that they are unaffected.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_one.users_unaffected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_one.users_unaffected)))
     sheila-shakespeare
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_two.users_unaffected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_two.users_unaffected)))
     sheila-shakespeare
 
 But they didn't file the third dup, so there was never any explicit
@@ -1304,16 +1304,16 @@
 as explicitly unaffected, even after marking the master bug as not
 affecting them.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_three.users_unaffected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_three.users_unaffected)))
     <BLANKLINE>
 
 However, if a dup was not marked either way for that user, then do
 nothing to the dup when the master is marked as not affecting the
 user.
 
-    >>> print '\n'.join(
-    ...     sorted(user.name for user in dupe_three.users_affected))
+    >>> print('\n'.join(
+    ...     sorted(user.name for user in dupe_three.users_affected)))
     napoleon-bonaparte
 
 
@@ -1338,7 +1338,7 @@
     ...     bug_tasks, user=sample_person)
     >>> bugs = sorted(bugs, key=attrgetter('title'))
     >>> for bug in bugs:
-    ...     print bug.title
+    ...     print(bug.title)
     New bug 0
     New bug 1
     New bug 2
@@ -1356,12 +1356,12 @@
     >>> len(matching_bugs)
     1
 
-    >>> print matching_bugs[0].title
+    >>> print(matching_bugs[0].title)
     New bug 0
 
     >>> for task in sorted(matching_bugs[0].bugtasks,
     ...     key=attrgetter('bugtargetname')):
-    ...     print task.bugtargetname
+    ...     print(task.bugtargetname)
     firefox
     thunderbird
 
@@ -1377,7 +1377,7 @@
     ...     bug_tasks, user=no_priv)
 
     >>> for bug in sorted(matching_bugs, key=attrgetter('title')):
-    ...     print bug.title
+    ...     print(bug.title)
     New bug 0
     New bug 1
     New bug 3
@@ -1394,7 +1394,7 @@
     ...     bug_tasks, user=no_priv)
 
     >>> for bug in sorted(matching_bugs, key=attrgetter('title')):
-    ...     print bug.title
+    ...     print(bug.title)
     New bug 0
     New bug 1
     New bug 4
@@ -1409,7 +1409,7 @@
     ...     bug_tasks, user=no_priv)
 
     >>> for bug in sorted(matching_bugs, key=attrgetter('title')):
-    ...     print bug.title
+    ...     print(bug.title)
     New bug 0
     New bug 1
 
@@ -1419,7 +1419,7 @@
     >>> matching_bugs = getUtility(IBugSet).getDistinctBugsForBugTasks(
     ...     bug_tasks, user=no_priv, limit=1)
     >>> for bug in sorted(matching_bugs, key=attrgetter('title')):
-    ...     print bug.title
+    ...     print(bug.title)
     New bug 0
 
 
@@ -1438,7 +1438,7 @@
 Bug.getHWSubmissions() returns the HWDB submissions linked to a bug.
 
     >>> for submission in test_bug.getHWSubmissions():
-    ...     print submission.submission_key
+    ...     print(submission.submission_key)
     sample-submission
 
 Private submissions are only included if the current user is an admin
@@ -1449,12 +1449,12 @@
     ...     submission_key='private-submission')
     >>> test_bug.linkHWSubmission(private_submission)
     >>> for submission in test_bug.getHWSubmissions(user=sample_person):
-    ...     print submission.submission_key
+    ...     print(submission.submission_key)
     private-submission
     sample-submission
 
     >>> for submission in test_bug.getHWSubmissions(user=foobar):
-    ...     print submission.submission_key
+    ...     print(submission.submission_key)
     private-submission
     sample-submission
 
@@ -1462,17 +1462,17 @@
 
     >>> no_priv = personset.getByEmail('no-priv@xxxxxxxxxxxxx')
     >>> for submission in test_bug.getHWSubmissions(user=no_priv):
-    ...     print submission.submission_key
+    ...     print(submission.submission_key)
     sample-submission
 
     >>> for submission in test_bug.getHWSubmissions():
-    ...     print submission.submission_key
+    ...     print(submission.submission_key)
     sample-submission
 
 We can also delete links between a HWDB submission and a bug.
 
     >>> test_bug.unlinkHWSubmission(submission)
-    >>> print test_bug.getHWSubmissions().count()
+    >>> print(test_bug.getHWSubmissions().count())
     0
 
 
@@ -1567,9 +1567,9 @@
 If a bug has no attachments, both properties are None.
 
     >>> bug = factory.makeBug()
-    >>> print bug.latest_patch
+    >>> print(bug.latest_patch)
     None
-    >>> print bug.latest_patch_uploaded
+    >>> print(bug.latest_patch_uploaded)
     None
 
 If we add an attachment that is not a patch, the value of
@@ -1582,7 +1582,7 @@
     >>> attachment_1 = factory.makeBugAttachment(bug)
 
     >>> transaction.commit()
-    >>> print bug.latest_patch_uploaded
+    >>> print(bug.latest_patch_uploaded)
     None
 
 If we declare the existing attachment to be a patch,
@@ -1594,9 +1594,9 @@
     >>> attachment_1.type = BugAttachmentType.PATCH
     >>> transaction.commit()
     >>> date_message_1_created = bug.attachments[0].message.datecreated
-    >>> print bug.latest_patch == attachment_1
+    >>> print(bug.latest_patch == attachment_1)
     True
-    >>> print bug.latest_patch_uploaded == date_message_1_created
+    >>> print(bug.latest_patch_uploaded == date_message_1_created)
     True
 
 If we add another attachment, this time declared to be a patch
@@ -1607,9 +1607,9 @@
     >>> attachment_2 = factory.makeBugAttachment(bug, is_patch=True)
     >>> transaction.commit()
     >>> date_message_2_created = bug.attachments[1].message.datecreated
-    >>> print bug.latest_patch == attachment_2
+    >>> print(bug.latest_patch == attachment_2)
     True
-    >>> print bug.latest_patch_uploaded == date_message_2_created
+    >>> print(bug.latest_patch_uploaded == date_message_2_created)
     True
 
 If we say that attachment_1 is not a patch, the values of
@@ -1617,9 +1617,9 @@
 
     >>> attachment_1.type = BugAttachmentType.UNSPECIFIED
     >>> transaction.commit()
-    >>> print bug.latest_patch == attachment_2
+    >>> print(bug.latest_patch == attachment_2)
     True
-    >>> print bug.latest_patch_uploaded == date_message_2_created
+    >>> print(bug.latest_patch_uploaded == date_message_2_created)
     True
 
 If we declare attachment_1 again to be a patch and if we delete
@@ -1629,9 +1629,9 @@
     >>> attachment_1.type = BugAttachmentType.PATCH
     >>> attachment_2.removeFromBug(user=bug.owner)
     >>> transaction.commit()
-    >>> print bug.latest_patch == attachment_1
+    >>> print(bug.latest_patch == attachment_1)
     True
-    >>> print bug.latest_patch_uploaded == date_message_1_created
+    >>> print(bug.latest_patch_uploaded == date_message_1_created)
     True
 
 If we delete attachment_1 too, bug.latest_patch and
@@ -1639,7 +1639,7 @@
 
     >>> attachment_1.removeFromBug(user=bug.owner)
     >>> transaction.commit()
-    >>> print bug.latest_patch
+    >>> print(bug.latest_patch)
     None
-    >>> print bug.latest_patch_uploaded
+    >>> print(bug.latest_patch_uploaded)
     None

=== modified file 'lib/lp/bugs/doc/bugactivity.txt'
--- lib/lp/bugs/doc/bugactivity.txt	2012-10-10 03:12:08 +0000
+++ lib/lp/bugs/doc/bugactivity.txt	2018-06-30 11:02:51 +0000
@@ -115,7 +115,7 @@
 whenever necessary), it is normalized to show the actual attribute name.
 For instance, look at the attributes on the previous activity.
 
-    >>> print bug.activity[-2].target
+    >>> print(bug.activity[-2].target)
     None
     >>> bug.activity[-2].whatchanged
     u'summary'
@@ -237,14 +237,14 @@
 final_bug as their master bug.
 
     >>> latest_activity = dupe_one.activity[-1]
-    >>> print latest_activity.whatchanged
+    >>> print(latest_activity.whatchanged)
     changed duplicate marker
     >>> latest_activity.oldvalue == unicode(initial_bug.id)
     True
     >>> latest_activity.newvalue == unicode(final_bug.id)
     True
     >>> latest_activity = dupe_two.activity[-1]
-    >>> print latest_activity.whatchanged
+    >>> print(latest_activity.whatchanged)
     changed duplicate marker
     >>> latest_activity.oldvalue == unicode(initial_bug.id)
     True
@@ -277,7 +277,7 @@
 activity sensibly in an HTML interface. In most cases it just returns
 activity.whatchanged.
 
-    >>> print activity_item.change_summary
+    >>> print(activity_item.change_summary)
     summary
 
 Summary changes are represented as unified diffs in the interface, in
@@ -285,15 +285,15 @@
 properly in the UI, they're returned with newline characters replaces
 with HTML line-breaks.
 
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     - Old value<br />+ New value
 
 BugActivityItem delegates to IBugActivity, so we can still access the
 original BugActivity's properties if we want.
 
-    >>> print "%s: %s => %s" % (
+    >>> print("%s: %s => %s" % (
     ...     activity_item.whatchanged, activity_item.oldvalue,
-    ...     activity_item.newvalue)
+    ...     activity_item.newvalue))
     summary: Old value => New value
 
 For simpler changes, activity_item.change_details will simply return the
@@ -306,7 +306,7 @@
     ...     datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
 
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     no &#8594; yes
 
     >>> activity = getUtility(IBugActivitySet).new(
@@ -314,7 +314,7 @@
     ...     newvalue='private', person=user, datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
 
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     public &#8594; private
 
 Tag changes use the _formatted_tags_change property of BugActivityItem
@@ -324,14 +324,14 @@
     ...     bug=bug_one, whatchanged='tags', oldvalue='tag1 tag2',
     ...     newvalue='tag1 tag3', person=user, datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
-    >>> print activity_item._formatted_tags_change
+    >>> print(activity_item._formatted_tags_change)
     added: tag3
     removed: tag2
 
 The change_details value for this change will be that returned by
 _formatted_tags_change but with newlines replaced by HTML line-breaks.
 
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     added: tag3<br />removed: tag2
 
 For changes to bug tasks, BugActivityItem returns the name of the attribute
@@ -343,12 +343,12 @@
     ...     newvalue='Triaged', person=user, datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
 
-    >>> print activity_item.change_summary
+    >>> print(activity_item.change_summary)
     status
 
 The change_details are expressed as a simple change.
 
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     New &#8594; Triaged
 
 For assignee changes, BugActivityItem will ensure that old or new values
@@ -358,7 +358,7 @@
     ...     bug=bug_one, whatchanged='malone: assignee', oldvalue=None,
     ...     newvalue='somebody', person=user, datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     nobody &#8594; somebody
 
     >>> activity = getUtility(IBugActivitySet).new(
@@ -366,7 +366,7 @@
     ...     oldvalue='somebody', newvalue=None, person=user,
     ...     datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
-    >>> print activity_item.change_details
+    >>> print(activity_item.change_details)
     somebody &#8594; nobody
 
 For changes to a bug's description, we simply return the word 'updated,'
@@ -377,6 +377,6 @@
     ...     oldvalue='Old description', newvalue='New description',
     ...     person=user, datechanged=nowish)
     >>> activity_item = BugActivityItem(activity)
-    >>> print "%s: %s" % (
-    ...     activity_item.change_summary, activity_item.change_details)
+    >>> print("%s: %s" % (
+    ...     activity_item.change_summary, activity_item.change_details))
     description: updated

=== modified file 'lib/lp/bugs/doc/bugattachments.txt'
--- lib/lp/bugs/doc/bugattachments.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bugattachments.txt	2018-06-30 11:02:51 +0000
@@ -26,17 +26,17 @@
 To create an attachment, call IBug.addAttachment. It will emit an
 ObjectCreatedEvent in order to trigger email notifications:
 
-    >>> from StringIO import StringIO
+    >>> from io import BytesIO
 
     >>> from lp.testing.event import TestEventListener
     >>> from lazr.lifecycle.event import IObjectCreatedEvent
     >>> def attachment_added(attachment, event):
-    ...     print "Attachment added: %r" % attachment.libraryfile.filename
+    ...     print("Attachment added: %r" % attachment.libraryfile.filename)
     >>> event_listener = TestEventListener(
     ...     IBugAttachment, IObjectCreatedEvent, attachment_added)
 
-    >>> filecontent = "Some useful information."
-    >>> data = StringIO(filecontent)
+    >>> filecontent = b"Some useful information."
+    >>> data = BytesIO(filecontent)
 
     >>> foobar = getUtility(IPersonSet).getByName("name16")
 
@@ -82,7 +82,7 @@
 
 If no description is given, the title is set to the filename.
 
-    >>> data = StringIO(filecontent)
+    >>> data = BytesIO(filecontent)
     >>> screenshot = bug_four.addAttachment(
     ...     owner=foobar,
     ...     data=data,
@@ -98,7 +98,7 @@
     >>> screenshot.libraryfile.mimetype
     u'image/jpeg'
 
-    >>> data = StringIO('</something-htmlish>')
+    >>> data = BytesIO(b'</something-htmlish>')
     >>> debdiff = bug_four.addAttachment(
     ...     owner=foobar,
     ...     data=data,
@@ -120,7 +120,7 @@
     >>> from lp.services.webapp.servers import LaunchpadTestRequest
 
     >>> login('test@xxxxxxxxxxxxx')
-    >>> filecontent = StringIO('')
+    >>> filecontent = BytesIO(b'')
     >>> filecontent.filename = 'foo.bar'
     >>> add_request = LaunchpadTestRequest(
     ...     method="POST",
@@ -151,7 +151,7 @@
 testrunner is 1024, so let's create a file larger than that and try to
 upload it:
 
-    >>> filecontent = StringIO('x'*1025)
+    >>> filecontent = BytesIO(b'x'*1025)
     >>> filecontent.filename = 'foo.txt'
     >>> add_request = LaunchpadTestRequest(
     ...     method="POST",
@@ -213,8 +213,8 @@
 
 If the request contains no attachment description the filename should be used.
 
-    >>> filecontent = StringIO(
-    ...     "No, sir. That's one bonehead name, but that ain't me any more.")
+    >>> filecontent = BytesIO(
+    ...     b"No, sir. That's one bonehead name, but that ain't me any more.")
     >>> filecontent.filename = 'RA.txt'
     >>> add_request = LaunchpadTestRequest(
     ...     method="POST",
@@ -237,7 +237,7 @@
 
     >>> from lp.bugs.model.bugnotification import BugNotification
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Attachment added: "RA.txt"
        http://.../RA.txt
 
@@ -452,7 +452,7 @@
 
 It's also possible to delete attachments.
 
-    >>> data = StringIO(filecontent)
+    >>> data = BytesIO(filecontent.getvalue())
     >>> bug_two = getUtility(IBugSet).get(2)
     >>> attachment = bug_two.addAttachment(
     ...     owner=foobar,
@@ -462,7 +462,7 @@
     ...     comment="a string comment",
     ...     is_patch=False)
     >>> for attachment in bug_two.attachments:
-    ...     print attachment.title
+    ...     print(attachment.title)
     Attachment to be deleted
 
     >>> libraryfile = attachment.libraryfile
@@ -485,7 +485,7 @@
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Attachment removed: "Attachment to be deleted"
        http://.../foo.baz
 
@@ -500,7 +500,7 @@
     0
     >>> attachment = bug_two.addAttachment(
     ...     owner=foobar,
-    ...     data=StringIO(filecontent),
+    ...     data=BytesIO(filecontent.getvalue()),
     ...     filename="foo.baz",
     ...     description="A non-patch attachment",
     ...     comment="a string comment",
@@ -511,7 +511,7 @@
     False
     >>> attachment = bug_two.addAttachment(
     ...     owner=foobar,
-    ...     data=StringIO(filecontent),
+    ...     data=BytesIO(filecontent.getvalue()),
     ...     filename="foo.baz",
     ...     description="A patch attachment",
     ...     comment="a string comment",
@@ -538,11 +538,11 @@
     >>> from lp.services.librarian.interfaces import (
     ...     ILibraryFileAliasSet)
 
-    >>> file_content = "Hello, world"
+    >>> file_content = b"Hello, world"
     >>> content_type = "text/plain"
     >>> file_alias = getUtility(ILibraryFileAliasSet).create(
     ...     name='foobar', size=len(file_content),
-    ...     file=StringIO(file_content), contentType=content_type)
+    ...     file=BytesIO(file_content), contentType=content_type)
     >>> transaction.commit()
 
     >>> bug = factory.makeBug()
@@ -554,13 +554,13 @@
     >>> bug.attachments.count()
     1
     >>> attachment = bug.attachments[0]
-    >>> print attachment.title
+    >>> print(attachment.title)
     foobar
 
 The attachment will have a type of BugAttachmentType.UNSPECIFIED, since
 we didn't specify that it was a patch.
 
-    >>> print attachment.type.title
+    >>> print(attachment.type.title)
     Unspecified
 
 We can specify that the attachment is a patch and give it a more
@@ -568,7 +568,7 @@
 
     >>> file_alias = getUtility(ILibraryFileAliasSet).create(
     ...     name='anotherfoobar', size=len(file_content),
-    ...     file=StringIO(file_content), contentType=content_type)
+    ...     file=BytesIO(file_content), contentType=content_type)
     >>> transaction.commit()
 
     >>> bug.linkAttachment(
@@ -580,10 +580,10 @@
     >>> bug.attachments.count()
     2
     >>> attachment = bug.attachments[1]
-    >>> print attachment.title
+    >>> print(attachment.title)
     An attachment of some sort
 
-    >>> print attachment.type.title
+    >>> print(attachment.type.title)
     Patch
 
 
@@ -620,7 +620,7 @@
     ...     information_type=InformationType.USERDATA,
     ...     owner=private_bug_owner)
     >>> private_attachment = private_bug.addAttachment(
-    ...     owner=private_bug_owner, data="secret", filename="baz.txt",
+    ...     owner=private_bug_owner, data=b"secret", filename="baz.txt",
     ...     comment="Some attachment")
     >>> private_attachment.libraryfile.restricted
     True
@@ -662,4 +662,4 @@
     >>> attachment.getFileByName('nonsense')
     Traceback (most recent call last):
     ...
-    NotFoundError: 'nonsense'
+    NotFoundError: u'nonsense'

=== modified file 'lib/lp/bugs/doc/bugcomment.txt'
--- lib/lp/bugs/doc/bugcomment.txt	2014-05-29 16:18:50 +0000
+++ lib/lp/bugs/doc/bugcomment.txt	2018-06-30 11:02:51 +0000
@@ -85,7 +85,7 @@
 
     >>> rendered_comments[0].text_for_display
     ''
-    >>> print rendered_comments[0].text_contents
+    >>> print(rendered_comments[0].text_contents)
     I've had problems when switching from Jokosher...
 
 
@@ -123,7 +123,7 @@
 display the truncated text using text_for_display:
 
     >>> comment_one = bug_comments(bug_view)[0]
-    >>> print comment_one.text_for_display #doctest: -ELLIPSIS
+    >>> print(comment_one.text_for_display) #doctest: -ELLIPSIS
     This would be a real...
 
 The UI will display information about the comment being truncated and
@@ -166,7 +166,7 @@
     >>> bug_11 = getUtility(IBugSet).get(11)
     >>> all_comments = get_comments_for_bugtask(bug_11.bugtasks[0])
     >>> for comment in all_comments:
-    ...     print comment.display_title, comment.title
+    ...     print(comment.display_title, comment.title)
     False Make Jokosher use autoaudiosink
     False Re: Make Jokosher use autoaudiosink
     False Re: Make Jokosher use autoaudiosink
@@ -177,7 +177,7 @@
     >>> bug_12 = getUtility(IBugSet).get(12)
     >>> all_comments = get_comments_for_bugtask(bug_12.bugtasks[0])
     >>> for comment in all_comments:
-    ...     print comment.display_title, comment.title
+    ...     print(comment.display_title, comment.title)
     False Copy, Cut and Delete operations should work on selections
     False Re: Copy, Cut and Delete operations should work on selections
     False Re: Copy, Cut and Delete operations should work on selections
@@ -353,8 +353,8 @@
     ...     activity=[activity_item])
 
     >>> for activity in bug_comment.activity:
-    ...     print "%s: %s" % (
-    ...         activity.change_summary, activity.change_details)
+    ...     print("%s: %s" % (
+    ...         activity.change_summary, activity.change_details))
     status: New &#8594; Confirmed
 
 The activity will be inserted into the footer of the comment. If a
@@ -411,10 +411,10 @@
 
     >>> bug_comment = bug_view.comments[0]
     >>> for attachment in bug_comment.bugattachments:
-    ...     print attachment.title, attachment.type.title
+    ...     print(attachment.title, attachment.type.title)
     sample data 1 Unspecified
     sample data 2 Unspecified
     >>> for patch in bug_comment.patches:
-    ...     print patch.title, patch.type.title
+    ...     print(patch.title, patch.type.title)
     patch 1 Patch
     patch 2 Patch

=== modified file 'lib/lp/bugs/doc/bugnotification-email.txt'
--- lib/lp/bugs/doc/bugnotification-email.txt	2015-10-06 06:48:01 +0000
+++ lib/lp/bugs/doc/bugnotification-email.txt	2018-06-30 11:02:51 +0000
@@ -58,7 +58,7 @@
     >>> subject
     u'[Bug 4] [NEW] Reflow problems with complex page layouts'
 
-    >>> print body
+    >>> print(body)
     Public bug reported:
     <BLANKLINE>
     Malone pages that use more complex layouts with portlets and fancy CSS
@@ -80,7 +80,7 @@
     >>> subject
     u'[Bug 4] [NEW] Reflow problems with complex page layouts'
 
-    >>> print body
+    >>> print(body)
     Public bug reported:
     <BLANKLINE>
     Malone pages that use more complex layouts with portlets and fancy CSS
@@ -101,7 +101,7 @@
     >>> subject
     u'[Bug 4] [NEW] Reflow problems with complex page layouts'
 
-    >>> print body
+    >>> print(body)
     *** This bug is a security vulnerability ***
     <BLANKLINE>
     Public security bug reported:
@@ -115,7 +115,7 @@
     True
 
     >>> subject, body = generate_bug_add_email(bug_four)
-    >>> print body
+    >>> print(body)
     *** This bug is a security vulnerability ***
     <BLANKLINE>
     Private security bug reported:
@@ -161,8 +161,8 @@
 
     >>> for change in get_bug_changes(bug_delta):
     ...        notification = change.getBugNotification()
-    ...        print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...        print "-----------------------------"
+    ...        print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...        print("-----------------------------")
     ** Summary changed:
     <BLANKLINE>
     - Blackhole Trash folder
@@ -201,8 +201,8 @@
     ...     })
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Description changed:
     <BLANKLINE>
     - The Trash folder seems to have significant problems! At the moment,
@@ -241,8 +241,8 @@
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
     ...     text_representation = notification['text']
-    ...     print text_representation #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(text_representation) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Information type changed from Public to Private Security
     -----------------------------
 
@@ -261,8 +261,8 @@
     ...         })
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Information type changed from Private Security to Public
     -----------------------------
 
@@ -277,8 +277,8 @@
     ...        tags={'old': old_tags, 'new': edited_bug.tags})
     >>> for change in get_bug_changes(bug_delta):
     ...        notification = change.getBugNotification()
-    ...        print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...        print "-----------------------------"
+    ...        print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...        print("-----------------------------")
     ** Tags added: bar foo
     -----------------------------
 
@@ -293,8 +293,8 @@
     ...        tags={'old': old_tags, 'new': edited_bug.tags})
     >>> for change in get_bug_changes(bug_delta):
     ...        notification = change.getBugNotification()
-    ...        print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...        print "-----------------------------"
+    ...        print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...        print("-----------------------------")
     ** Tags removed: bar
     ** Tags added: baz
     -----------------------------
@@ -335,8 +335,8 @@
     ...     bugtask_deltas=bugtask_delta)
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Changed in: firefox
            Status: New => Confirmed
     -----------------------------
@@ -348,7 +348,7 @@
 edited:
 
     >>> debian_bugtask = getUtility(IBugTaskSet).get(5)
-    >>> print debian_bugtask.bugtargetname
+    >>> print(debian_bugtask.bugtargetname)
     mozilla-firefox (Debian)
 
     >>> debian_bugtask.transitionToAssignee(None)
@@ -362,8 +362,8 @@
     ...     bugtask_deltas=bugtask_delta)
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Changed in: mozilla-firefox (Debian)
          Assignee: Sample Person (name12) => (unassigned)
     -----------------------------
@@ -384,8 +384,8 @@
     ...     attachment={'new' : attachment, 'old': None})
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Attachment added: "A screenshot of the problem"
        http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/screenshot.png
     -----------------------------
@@ -399,8 +399,8 @@
     ...     attachment={'old' : attachment, 'new': None})
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Attachment removed: "A screenshot of the problem"
        http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/screenshot.png
     -----------------------------
@@ -418,8 +418,8 @@
     ...     attachment={'new' : attachment, 'old': None})
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Patch added: "A new icon for the application"
        http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/new-icon.png
     -----------------------------
@@ -433,8 +433,8 @@
     ...     attachment={'old' : attachment, 'new': None})
     >>> for change in get_bug_changes(bug_delta):
     ...     notification = change.getBugNotification()
-    ...     print notification['text'] #doctest: -NORMALIZE_WHITESPACE
-    ...     print "-----------------------------"
+    ...     print(notification['text']) #doctest: -NORMALIZE_WHITESPACE
+    ...     print("-----------------------------")
     ** Patch removed: "A new icon for the application"
        http://bugs.launchpad.dev/bugs/.../+attachment/.../+files/new-icon.png
     -----------------------------
@@ -520,7 +520,7 @@
     >>> bug_four_notification_builder = BugNotificationBuilder(bug_four,
     ...     private_person)
     >>> for header in bug_four_notification_builder.common_headers:
-    ...     print ': '.join(header)
+    ...     print(': '.join(header))
     Reply-To: Bug 4 <4@xxxxxxxxxxxxxxxxxx>
     Sender: bounces@xxxxxxxxxxxxx
     X-Launchpad-Notification-Type: bug
@@ -554,19 +554,19 @@
 the parameters that were used to instantiate BugNotificationBuilder and
 passed to <builder>.build().
 
-    >>> print notification_email['From']
+    >>> print(notification_email['From'])
     Launchpad Bug Tracker <4@xxxxxxxxxxxxxxxxxx>
 
-    >>> print notification_email['To']
+    >>> print(notification_email['To'])
     foo.bar@xxxxxxxxxxxxx
 
-    >>> print notification_email['Subject']
+    >>> print(notification_email['Subject'])
     [Bug 4] A test subject.
 
-    >>> print notification_email['Date']
+    >>> print(notification_email['Date'])
     Tue, 20 May 2008 09:05:47 -0000
 
-    >>> print notification_email.get_payload()
+    >>> print(notification_email.get_payload())
     A test body.
 
 The <builder>.build() method also accepts parameters for rationale,
@@ -582,24 +582,24 @@
 The X-Launchpad-Message-Rationale header is set from the rationale
 parameter.
 
-    >>> print notification_email['X-Launchpad-Message-Rationale']
+    >>> print(notification_email['X-Launchpad-Message-Rationale'])
     Because-I-said-so
 
 The X-Launchpad-Message-For header is set from the to_person (since this
 notification is not for a team).
 
-    >>> print notification_email['X-Launchpad-Message-For']
+    >>> print(notification_email['X-Launchpad-Message-For'])
     name16
 
 The references parameter sets the References header of the email.
 
-    >>> print notification_email['References']
+    >>> print(notification_email['References'])
     <12345@xxxxxxxxxxxxx>
 
 And the message_id parameter is used to set the Message-Id header. It
 will be auto-generated if it is not supplied.
 
-    >>> print notification_email['Message-Id']
+    >>> print(notification_email['Message-Id'])
     <67890@xxxxxxxxxxxxx>
 
 The message subject will always have [Bug <bug_id>] prepended to it.
@@ -608,7 +608,7 @@
     ...     from_address, to_person,
     ...     "A test body.", "Yet another message", sending_date)
 
-    >>> print notification_email['Subject']
+    >>> print(notification_email['Subject'])
     [Bug 4] Yet another message
 
 If the subject passed is None the email subject will be set to [Bug
@@ -617,5 +617,5 @@
     >>> notification_email = bug_four_notification_builder.build(
     ...     from_address, to_person, "A test body.", None, sending_date)
 
-    >>> print notification_email['Subject']
+    >>> print(notification_email['Subject'])
     [Bug 4]

=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt	2018-06-30 11:02:51 +0000
@@ -25,14 +25,14 @@
     ...                    'X-Launchpad-Message-For',
     ...                    'X-Launchpad-Subscription'] + extra_headers:
     ...         if email_notification[header]:
-    ...             print "%s: %s" % (header, email_notification[header])
+    ...             print("%s: %s" % (header, email_notification[header]))
 
     >>> def print_notification(email_notification, extra_headers=[]):
     ...     print_notification_headers(
     ...         email_notification, extra_headers=extra_headers)
-    ...     print
-    ...     print email_notification.get_payload(decode=True)
-    ...     print "-" * 70
+    ...     print()
+    ...     print(email_notification.get_payload(decode=True))
+    ...     print("-" * 70)
 
 We'll also import a helper function to help us with database users.
 
@@ -105,7 +105,7 @@
 message id as its reference, in order to make it thread properly in the
 email client.
 
-    >>> print bug_one.initial_message.rfc822msgid
+    >>> print(bug_one.initial_message.rfc822msgid)
     sdsdfsfd
 
 The notification is still pending to be sent, since date_emailed is
@@ -314,7 +314,7 @@
     >>> addressless.preferredemail is None
     True
     >>> for member in addressless.activemembers:
-    ...     print member.preferredemail.email
+    ...     print(member.preferredemail.email)
     owner@xxxxxxxxxxx
 
     >>> with lp_dbuser():
@@ -332,7 +332,7 @@
     >>> email_notifications = get_email_notifications(pending_notifications)
     >>> for bug_notifications, omitted, messages in email_notifications:
     ...     for message in messages:
-    ...         print message['To']
+    ...         print(message['To'])
     foo.bar@xxxxxxxxxxxxx
     mark@xxxxxxxxxxx
     owner@xxxxxxxxxxx
@@ -538,7 +538,7 @@
     >>> (out, err) = process.communicate()
     >>> process.returncode
     0
-    >>> print err
+    >>> print(err)
     DEBUG   ...
     INFO    Notifying test@xxxxxxxxxxxxx about bug 2.
     ...
@@ -624,7 +624,7 @@
     ...         identifier = 'comment'
     ...     else:
     ...         identifier = notification.activity.whatchanged
-    ...     print identifier, notification.status.title
+    ...     print(identifier, notification.status.title)
     comment Sent
     summary Sent
     comment Sent
@@ -744,15 +744,15 @@
 and X-Launchpad-Bug-Security-Vulnerability headers are also set. These headers
 can have the value "yes" or "no".
 
-    >>> print bug_three.information_type.title
+    >>> print(bug_three.information_type.title)
     Public
 
     >>> def print_message_header_details(message):
-    ...     print '%s %s %s %s' % (
+    ...     print('%s %s %s %s' % (
     ...         message['To'],
     ...         message.get_all('X-Launchpad-Bug-Private'),
     ...         message.get_all('X-Launchpad-Bug-Security-Vulnerability'),
-    ...         message.get_all('X-Launchpad-Bug-Information-Type'))
+    ...         message.get_all('X-Launchpad-Bug-Information-Type')))
 
     >>> for message in trigger_and_get_email_messages(bug_three):
     ...     print_message_header_details(message)
@@ -764,7 +764,7 @@
     ...     bug_three.transitionToInformationType(
     ...         InformationType.USERDATA, sample_person)
     True
-    >>> print bug_three.information_type.title
+    >>> print(bug_three.information_type.title)
     Private
 
     >>> for message in trigger_and_get_email_messages(bug_three):
@@ -777,7 +777,7 @@
     ...     bug_three.transitionToInformationType(
     ...         InformationType.PRIVATESECURITY, getUtility(ILaunchBag).user)
     True
-    >>> print bug_three.information_type.title
+    >>> print(bug_three.information_type.title)
     Private Security
 
     >>> for message in trigger_and_get_email_messages(bug_three):
@@ -793,7 +793,7 @@
 list.
 
     >>> message = trigger_and_get_email_messages(bug_three)[0]
-    >>> print message.get('X-Launchpad-Bug-Commenters')
+    >>> print(message.get('X-Launchpad-Bug-Commenters'))
     name12
 
     >>> from lp.registry.interfaces.person import IPersonSet
@@ -805,7 +805,7 @@
     ...         'Hungry', bug_three, foo_bar, "Make me a sandwich.")
 
     >>> message = trigger_and_get_email_messages(bug_three)[0]
-    >>> print message.get('X-Launchpad-Bug-Commenters')
+    >>> print(message.get('X-Launchpad-Bug-Commenters'))
     name12 name16
 
 It only lists each user once, no matter how many comments they've
@@ -816,7 +816,7 @@
     ...         'Hungry', bug_three, foo_bar, "Make me a sandwich.")
 
     >>> message = trigger_and_get_email_messages(bug_three)[0]
-    >>> print message.get('X-Launchpad-Bug-Commenters')
+    >>> print(message.get('X-Launchpad-Bug-Commenters'))
     name12 name16
 
 
@@ -827,7 +827,7 @@
 user who originally reported the bug and opened the bug's first bug task.
 
     >>> message = trigger_and_get_email_messages(bug_three)[0]
-    >>> print message.get('X-Launchpad-Bug-Reporter')
+    >>> print(message.get('X-Launchpad-Bug-Reporter'))
     Foo Bar (name16)
 
 
@@ -1058,25 +1058,25 @@
 
     >>> message = collated_messages['conciseteam@xxxxxxxxxxx'][0]
     >>> payload = message.get_payload(decode=True)
-    >>> print payload.split('\n')
+    >>> print(payload.split('\n'))
     [...
-     'Title:',
-     '  In the beginning, the universe was created. This has made a lot of',
-     '  people very angry and has been widely regarded as a bad move',
+     u'Title:',
+     u'  In the beginning, the universe was created. This has made a lot of',
+     u'  people very angry and has been widely regarded as a bad move',
      ...
-     'Bug description:',
-     '  This is a long description of the bug, which will be automatically',
-     "  wrapped by the BugNotification machinery. Ain't technology great?"...]
+     u'Bug description:',
+     u'  This is a long description of the bug, which will be automatically',
+     u"  wrapped by the BugNotification machinery. Ain't technology great?"...]
 
 The title is also wrapped and indented in normal notifications.
 
     >>> message = collated_messages['verboseteam@xxxxxxxxxxx'][0]
     >>> payload = message.get_payload(decode=True)
-    >>> print payload.strip().split('\n')
+    >>> print(payload.strip().split('\n'))
     [...
-     'Title:',
-     '  In the beginning, the universe was created. This has made a lot of',
-     '  people very angry and has been widely regarded as a bad move'...]
+     u'Title:',
+     u'  In the beginning, the universe was created. This has made a lot of',
+     u'  people very angry and has been widely regarded as a bad move'...]
 
 Self-Generated Bug Notifications
 --------------------------------
@@ -1127,7 +1127,7 @@
 have any filters other than the initial catch-all one, so they receive these
 notifications.
 
-    >>> print subscription_no_priv.bug_filters.count()
+    >>> print(subscription_no_priv.bug_filters.count())
     1
     >>> comment = getUtility(IMessageSet).fromText(
     ...     'subject', 'another comment.', sample_person,

=== modified file 'lib/lp/bugs/doc/bugnotifications.txt'
--- lib/lp/bugs/doc/bugnotifications.txt	2017-05-31 17:31:58 +0000
+++ lib/lp/bugs/doc/bugnotifications.txt	2018-06-30 11:02:51 +0000
@@ -43,12 +43,12 @@
 
     >>> from lp.bugs.model.bugnotification import BugNotification
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     True
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     this is a comment
 
 Notifications usually have references to a corresponding bug activity.  These
@@ -56,9 +56,9 @@
 perspective.  You can get details on what the activity provides in
 bugactivity.txt, but for now here is a small demo.
 
-    >>> print latest_notification.activity.whatchanged
+    >>> print(latest_notification.activity.whatchanged)
     bug
-    >>> print latest_notification.activity.person.displayname
+    >>> print(latest_notification.activity.person.displayname)
     Sample Person
 
 
@@ -74,19 +74,19 @@
     >>> notify(firefox_crashes_modified)
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Description changed:
     ...
-    >>> print latest_notification.activity.attribute
+    >>> print(latest_notification.activity.attribute)
     description
-    >>> print latest_notification.activity.oldvalue
+    >>> print(latest_notification.activity.oldvalue)
     this is a comment
-    >>> print latest_notification.activity.newvalue
+    >>> print(latest_notification.activity.newvalue)
     a new description
 
 
@@ -113,12 +113,12 @@
     ...     firefox_crashes_in_debian, firefox_crashes_in_debian.owner))
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Also affects: mozilla-firefox (Debian)
     ...
 
@@ -139,12 +139,12 @@
     ...     firefox_crashes_in_sid, firefox_crashes_in_sid.owner))
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Also affects: mozilla-firefox (Debian Sid)
     ...
 
@@ -167,12 +167,12 @@
     ...     evolution_crashes_too, evolution_crashes_too.owner))
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Also affects: evolution
     ...
 
@@ -189,12 +189,12 @@
     ...     firefox_crashes_in_trunk, firefox_crashes_in_trunk.owner))
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Also affects: evolution/trunk
     ...
 
@@ -211,12 +211,12 @@
     >>> notify(ObjectCreatedEvent(comment_on_firefox_crashes_in_debian))
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     True
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     just a test comment
 
 
@@ -242,12 +242,12 @@
     >>> notify(debian_task_modified)
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Changed in: mozilla-firefox (Debian)
     ...
 
@@ -262,12 +262,12 @@
     >>> notify(firefox_task_modified)
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** Changed in: evolution/trunk
     ...
 
@@ -287,12 +287,12 @@
     >>> bugcve = cve.linkBug(bug) # note this creates the event and notifies
 
     >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print latest_notification.message.owner.displayname
+    >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
     >>> latest_notification.is_comment
     False
-    >>> print latest_notification.message.text_contents
+    >>> print(latest_notification.message.text_contents)
     ** CVE added: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2004-0276
 
 

=== modified file 'lib/lp/bugs/doc/bugsubscription.txt'
--- lib/lp/bugs/doc/bugsubscription.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bugsubscription.txt	2018-06-30 11:02:51 +0000
@@ -57,11 +57,11 @@
     >>> linux_source = ubuntu.getSourcePackage("linux-source-2.6.15")
     >>> list(linux_source.bug_subscriptions)
     []
-    >>> print linux_source.distribution.bug_supervisor
+    >>> print(linux_source.distribution.bug_supervisor)
     None
 
     >>> foobar = getUtility(ILaunchBag).user
-    >>> print foobar.name
+    >>> print(foobar.name)
     name16
 
     >>> params = CreateBugParams(
@@ -76,7 +76,7 @@
     ...     subscriber_names = sorted(subscriber.displayname
     ...                               for subscriber in subscribers)
     ...     for name in subscriber_names:
-    ...         print name
+    ...         print(name)
 
     >>> print_displayname(linux_source_bug.getDirectSubscribers())
     Foo Bar
@@ -243,7 +243,7 @@
 In the case of bug notification levels, that is equivalent to
 BugNotificationLevel.COMMENTS.
 
-    >>> print subscription_no_priv.bug_filters.count()
+    >>> print(subscription_no_priv.bug_filters.count())
     1
 
 With this subscription level, No Privileges Person is returned for all
@@ -459,7 +459,7 @@
 By default, the bug_notification_level of the new subscription will be
 COMMENTS, so the user will receive all notifications about the bug.
 
-    >>> print subscription.bug_notification_level.name
+    >>> print(subscription.bug_notification_level.name)
     COMMENTS
 
 It's possible to subscribe to a bug at a different BugNotificationLevel
@@ -471,7 +471,7 @@
     ...     metadata_subscriber, metadata_subscriber,
     ...     level=BugNotificationLevel.METADATA)
 
-    >>> print metadata_subscription.bug_notification_level.name
+    >>> print(metadata_subscription.bug_notification_level.name)
     METADATA
 
 To unsubscribe from all dupes for a bug, call
@@ -640,7 +640,7 @@
     >>> ubuntu_task = getUtility(IBugTaskSet).createTask(
     ...     new_bug, mark, ubuntu)
 
-    >>> print '\n'.join(getSubscribers(new_bug))
+    >>> print('\n'.join(getSubscribers(new_bug)))
     foo.bar@xxxxxxxxxxxxx
 
 But still, only Foo Bar is explicitly subscribed.
@@ -669,14 +669,14 @@
 But the product owner, Sample Person, is implicitly added to the
 recipient list:
 
-    >>> print '\n'.join(getSubscribers(new_bug))
+    >>> print('\n'.join(getSubscribers(new_bug)))
     foo.bar@xxxxxxxxxxxxx
     >>> params = CreateBugParams(
     ...     title="a test bug", comment="a test description",
     ...     owner=foobar, information_type=InformationType.PRIVATESECURITY)
     >>> new_bug = firefox.createBug(params)
 
-    >>> print '\n'.join(getSubscribers(new_bug))
+    >>> print('\n'.join(getSubscribers(new_bug)))
     foo.bar@xxxxxxxxxxxxx
 
 Now let's create a bug on a specific package, which has no package bug
@@ -761,7 +761,7 @@
     ...         subscription.display_subscribed_by)
     ...     for subscription in ff_bug.getDirectSubscriptions()]
     >>> for subscription in sorted(subscriptions):
-    ...     print subscription
+    ...     print(subscription)
     Mark Shuttleworth: Self-subscribed
     Robert Collins: Subscribed by Mark Shuttleworth (mark)
     >>> params = CreateBugParams(
@@ -773,8 +773,8 @@
     >>> dupe_ff_bug.subscribe(foobar, lifeless)
     <lp.bugs.model.bugsubscription.BugSubscription ...>
     >>> for subscription in ff_bug.getSubscriptionsFromDuplicates():
-    ...     print '%s: %s' % (
+    ...     print('%s: %s' % (
     ...         subscription.person.displayname,
-    ...         subscription.display_duplicate_subscribed_by)
+    ...         subscription.display_duplicate_subscribed_by))
     Scott James Remnant: Self-subscribed to bug ...
     Foo Bar: Subscribed to bug ... by Robert Collins (lifeless)

=== modified file 'lib/lp/bugs/doc/bugsummary.txt'
--- lib/lp/bugs/doc/bugsummary.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/bugs/doc/bugsummary.txt	2018-06-30 11:02:51 +0000
@@ -84,9 +84,9 @@
     ...     fmt += ' %3s'
     ...     titles += ('#',)
     ...     header = fmt % titles
-    ...     print "-" * len(header)
-    ...     print header
-    ...     print "-" * len(header)
+    ...     print("-" * len(header))
+    ...     print(header)
+    ...     print("-" * len(header))
     ...     for bugsummary in ordered_results:
     ...         if not include_privacy:
     ...             assert bugsummary.viewed_by is None
@@ -107,12 +107,12 @@
     ...                 name(bugsummary.viewed_by),
     ...                 ap_desc(bugsummary.access_policy),
     ...                 )
-    ...         print fmt % (data + (bugsummary.count,))
-    ...     print " " * (len(header) - 4),
-    ...     print "==="
+    ...         print(fmt % (data + (bugsummary.count,)))
+    ...     print(" " * (len(header) - 4), end=" ")
+    ...     print("===")
     ...     sum = bugsummary_resultset.sum(BugSummary.count)
-    ...     print " " * (len(header) - 4),
-    ...     print "%3s" % sum
+    ...     print(" " * (len(header) - 4), end=" ")
+    ...     print("%3s" % sum)
 
     >>> def print_find(*bs_query_args, **bs_query_kw):
     ...     include_privacy = bs_query_kw.pop('include_privacy', False)
@@ -269,9 +269,9 @@
     ...     ).group_by(Milestone).order_by(Milestone.name)
     >>> for milestone, count in results:
     ...     if milestone:
-    ...         print milestone.name, count
+    ...         print(milestone.name, count)
     ...     else:
-    ...         print None, count
+    ...         print(None, count)
     ms-a 1
     None 4
 

=== modified file 'lib/lp/bugs/doc/bugtarget.txt'
--- lib/lp/bugs/doc/bugtarget.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/bugs/doc/bugtarget.txt	2018-06-30 11:02:51 +0000
@@ -36,25 +36,25 @@
     >>> IBugTarget.providedBy(debian_woody_firefox)
     True
 
-    >>> print firefox.bugtargetdisplayname
+    >>> print(firefox.bugtargetdisplayname)
     Mozilla Firefox
-    >>> print firefox.bugtargetname
+    >>> print(firefox.bugtargetname)
     firefox
-    >>> print firefox_1_0.bugtargetdisplayname
+    >>> print(firefox_1_0.bugtargetdisplayname)
     Mozilla Firefox 1.0
-    >>> print firefox_1_0.bugtargetname
+    >>> print(firefox_1_0.bugtargetname)
     firefox/1.0
-    >>> print debian.bugtargetdisplayname
+    >>> print(debian.bugtargetdisplayname)
     Debian
-    >>> print debian.bugtargetname
+    >>> print(debian.bugtargetname)
     debian
-    >>> print debian_firefox.bugtargetdisplayname
-    mozilla-firefox (Debian)
-    >>> print debian_firefox.bugtargetname
-    mozilla-firefox (Debian)
-    >>> print debian_woody.bugtargetdisplayname
+    >>> print(debian_firefox.bugtargetdisplayname)
+    mozilla-firefox (Debian)
+    >>> print(debian_firefox.bugtargetname)
+    mozilla-firefox (Debian)
+    >>> print(debian_woody.bugtargetdisplayname)
     Debian Woody
-    >>> print debian_woody_firefox.bugtargetdisplayname
+    >>> print(debian_woody_firefox.bugtargetdisplayname)
     mozilla-firefox (Debian Woody)
-    >>> print debian_woody_firefox.bugtargetname
+    >>> print(debian_woody_firefox.bugtargetname)
     mozilla-firefox (Debian Woody)

=== modified file 'lib/lp/bugs/doc/bugtask-assignee-widget.txt'
--- lib/lp/bugs/doc/bugtask-assignee-widget.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/bugs/doc/bugtask-assignee-widget.txt	2018-06-30 11:02:51 +0000
@@ -116,15 +116,15 @@
     >>> widget.name
     'field.assignee'
     >>> widget.setPrefix('foo')
-    >>> widget.name
-    'foo.assignee'
+    >>> print(widget.name)
+    foo.assignee
 
 The chooser widget got its name updated as well.
 
-    >>> widget.assignee_chooser_widget.name
-    'foo.assignee'
-    >>> widget.assignee_chooser_widget.onKeyPress
-    "selectWidget('foo.assignee.assign_to', event)"
+    >>> print(widget.assignee_chooser_widget.name)
+    foo.assignee
+    >>> print(widget.assignee_chooser_widget.onKeyPress)
+    selectWidget('foo.assignee.assign_to', event)
 
 If this option is selected, but no value is entered in
 "field.assignee", validation will fail:

=== modified file 'lib/lp/bugs/doc/bugtask-bugwatch-widget.txt'
--- lib/lp/bugs/doc/bugtask-bugwatch-widget.txt	2017-10-21 18:14:14 +0000
+++ lib/lp/bugs/doc/bugtask-bugwatch-widget.txt	2018-06-30 11:02:51 +0000
@@ -40,7 +40,7 @@
     ...         selected = 'SELECTED'
     ...     else:
     ...         selected = 'NOT SELECTED'
-    ...     print "%s: %s" % (selected, label_td.label)
+    ...     print("%s: %s" % (selected, label_td.label))
 
 Now if we pass None to renderItems(), the first radio button will be
 selected.
@@ -94,8 +94,8 @@
 the bug.
 
     >>> for bug_watch in bug_one.watches:
-    ...     print "%s: #%s" % (
-    ...         bug_watch.bugtracker.title, bug_watch.remotebug)
+    ...     print("%s: #%s" % (
+    ...         bug_watch.bugtracker.title, bug_watch.remotebug))
     The Mozilla.org Bug Tracker: #123543
     The Mozilla.org Bug Tracker: #2000
     The Mozilla.org Bug Tracker: #42
@@ -117,8 +117,8 @@
     u'84'
 
     >>> for bug_watch in bug_one.watches:
-    ...     print "%s: #%s" % (
-    ...         bug_watch.bugtracker.title, bug_watch.remotebug)
+    ...     print("%s: #%s" % (
+    ...         bug_watch.bugtracker.title, bug_watch.remotebug))
     The Mozilla.org Bug Tracker: #123543
     The Mozilla.org Bug Tracker: #2000
     The Mozilla.org Bug Tracker: #42
@@ -140,8 +140,8 @@
     u'84'
 
     >>> for bug_watch in bug_one.watches:
-    ...     print "%s: #%s" % (
-    ...         bug_watch.bugtracker.title, bug_watch.remotebug)
+    ...     print("%s: #%s" % (
+    ...         bug_watch.bugtracker.title, bug_watch.remotebug))
     The Mozilla.org Bug Tracker: #123543
     The Mozilla.org Bug Tracker: #2000
     The Mozilla.org Bug Tracker: #42

=== modified file 'lib/lp/bugs/doc/bugtask-display-widgets.txt'
--- lib/lp/bugs/doc/bugtask-display-widgets.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bugtask-display-widgets.txt	2018-06-30 11:02:51 +0000
@@ -22,7 +22,7 @@
 
     >>> assignee_field = IBugTask['assignee'].bind(firefox_bugtask)
     >>> assignee_widget = AssigneeDisplayWidget(assignee_field, None, None)
-    >>> print assignee_widget()
+    >>> print(assignee_widget())
     <a href="http://.../~mark";><img
         style="padding-bottom: 2px" alt="" src="/@@/person" />
       Mark Shuttleworth</a>
@@ -33,7 +33,7 @@
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> foo_bar = getUtility(IPersonSet).getByEmail('foo.bar@xxxxxxxxxxxxx')
     >>> assignee_widget.setRenderedValue(foo_bar)
-    >>> print assignee_widget()
+    >>> print(assignee_widget())
     <a href="http://.../~name16";><img
         style="padding-bottom: 2px" alt="" src="/@@/person" />
       Foo Bar</a>
@@ -43,7 +43,7 @@
     >>> firefox_ubuntu_bugtask = bug_one.bugtasks[1]
     >>> assignee_field = IBugTask['assignee'].bind(firefox_ubuntu_bugtask)
     >>> assignee_widget = AssigneeDisplayWidget(assignee_field, None, None)
-    >>> print assignee_widget()
+    >>> print(assignee_widget())
     <i>not assigned</i>
 
 If it's a remote bug task, where the information is pulled from a bug
@@ -56,7 +56,7 @@
     False
     >>> assignee_field = IBugTask['assignee'].bind(thunderbird_bugtask)
     >>> assignee_widget = AssigneeDisplayWidget(assignee_field, None, None)
-    >>> print assignee_widget()
+    >>> print(assignee_widget())
     <i>unknown</i>
 
 DBItemDisplayWidget
@@ -71,7 +71,7 @@
     'New'
     >>> status_field = IBugTask['status'].bind(firefox_bugtask)
     >>> status_widget = DBItemDisplayWidget(status_field, None, None)
-    >>> print status_widget()
+    >>> print(status_widget())
     <span class="statusNEW">New</span>
 
 If the value is None, a dash is displayed:
@@ -81,12 +81,12 @@
     True
     >>> assignee_field = IBugTask['assignee'].bind(test_task)
     >>> assignee_widget = DBItemDisplayWidget(assignee_field, None, None)
-    >>> print assignee_widget()
+    >>> print(assignee_widget())
     <span>&mdash;</span>
 
 We can set a specific value to be displayed using setRenderedValue():
 
     >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
     >>> status_widget.setRenderedValue(BugTaskStatus.CONFIRMED)
-    >>> print status_widget()
+    >>> print(status_widget())
     <span class="statusCONFIRMED">Confirmed</span>

=== modified file 'lib/lp/bugs/doc/bugtask-expiration.txt'
--- lib/lp/bugs/doc/bugtask-expiration.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bugtask-expiration.txt	2018-06-30 11:02:51 +0000
@@ -392,7 +392,7 @@
     >>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
     ...     0, user=None, target=ubuntu)
     >>> visible_bugs = set(bugtask.bug for bugtask in expirable_bugtasks)
-    >>> print sorted(bug.title for bug in visible_bugs)
+    >>> print(sorted(bug.title for bug in visible_bugs))
     [u'expirable_distro', u'recent']
 
 If one of the bugs is set to private, anonymous users can no longer see
@@ -408,7 +408,7 @@
     >>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
     ...     0, user=None, target=ubuntu)
     >>> visible_bugs = set(bugtask.bug for bugtask in expirable_bugtasks)
-    >>> print sorted(bug.title for bug in visible_bugs)
+    >>> print(sorted(bug.title for bug in visible_bugs))
     [u'recent']
 
 No Privileges Person can't see the bug either...
@@ -418,7 +418,7 @@
     >>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
     ...     0, user=no_priv, target=ubuntu)
     >>> visible_bugs = set(bugtask.bug for bugtask in expirable_bugtasks)
-    >>> print sorted(bug.title for bug in visible_bugs)
+    >>> print(sorted(bug.title for bug in visible_bugs))
     [u'recent']
 
 ... unless they're subscribed to the bug.
@@ -429,7 +429,7 @@
     >>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
     ...     0, user=no_priv, target=ubuntu)
     >>> visible_bugs = set(bugtask.bug for bugtask in expirable_bugtasks)
-    >>> print sorted(bug.title for bug in visible_bugs)
+    >>> print(sorted(bug.title for bug in visible_bugs))
     [u'expirable_distro', u'recent']
 
 The Janitor needs to be able to access all bugs, even private ones, in
@@ -444,7 +444,7 @@
     >>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
     ...     0, user=janitor, target=ubuntu)
     >>> visible_bugs = set(bugtask.bug for bugtask in expirable_bugtasks)
-    >>> print sorted(bug.title for bug in visible_bugs)
+    >>> print(sorted(bug.title for bug in visible_bugs))
     [u'expirable_distro', u'recent']
 
     >>> private_bug.setPrivate(False, sample_person)
@@ -497,7 +497,7 @@
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
     ...     stderr=subprocess.PIPE)
     >>> (out, err) = process.communicate()
-    >>> print err
+    >>> print(err)
     INFO    Creating lockfile: /var/lock/launchpad-expire-bugtasks.lock
     INFO    Expiring unattended, INCOMPLETE bugtasks older than
             60 days for projects that use Launchpad Bugs.
@@ -505,7 +505,7 @@
     INFO    Expired 2 bugtasks.
     INFO    Finished expiration run.
     <BLANKLINE>
-    >>> print out
+    >>> print(out)
     <BLANKLINE>
     >>> process.returncode
     0
@@ -546,18 +546,18 @@
     3
 
     >>> message = hoary_bugtask.bug.messages[-1]
-    >>> print message.owner.name
+    >>> print(message.owner.name)
     janitor
 
-    >>> print message.text_contents
+    >>> print(message.text_contents)
     [Expired for Ubuntu Hoary because there has been no activity for 60 days.]
 
 The bug's activity log was updated too with the status change.
 
     >>> activity = hoary_bugtask.bug.activity[-1]
-    >>> print "%s  %s  %s  %s" % (
+    >>> print("%s  %s  %s  %s" % (
     ...     activity.person.displayname, activity.whatchanged,
-    ...     activity.oldvalue, activity.newvalue)
+    ...     activity.oldvalue, activity.newvalue))
     Launchpad Janitor  Ubuntu Hoary: status  Incomplete  Expired
 
 

=== modified file 'lib/lp/bugs/doc/bugtask-find-similar.txt'
--- lib/lp/bugs/doc/bugtask-find-similar.txt	2012-08-08 11:48:29 +0000
+++ lib/lp/bugs/doc/bugtask-find-similar.txt	2018-06-30 11:02:51 +0000
@@ -40,7 +40,7 @@
     >>> similar_bugs = getUtility(IBugTaskSet).findSimilar(
     ...     test_person, "Can't display SVG", product=test_product)
     >>> for bugtask in similar_bugs:
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     SVG doesn't work
 
 
@@ -77,7 +77,7 @@
     ...     test_person, "sandwiches",
     ...     distribution=test_distro)
     >>> for bugtask in similar_bugs:
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     Nothing to do with cheese or sandwiches
     A bug about sandwiches
     This cheese sandwich should show up
@@ -111,7 +111,7 @@
     ...     another_user, "sandwiches",
     ...     distribution=test_distro)
     >>> for bugtask in similar_bugs:
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     A bug about sandwiches
     This cheese sandwich should show up
 
@@ -138,7 +138,7 @@
     ...     test_person, "cheese sandwiches show",
     ...     distribution=test_distro)
     >>> for bugtask in similar_bugs:
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     Nothing to do with cheese or sandwiches
     This cheese sandwich should show up
 
@@ -146,7 +146,7 @@
     ...     test_person, "Nothing sandwich",
     ...     distribution=test_distro)
     >>> for bugtask in similar_bugs:
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     Nothing to do with cheese or sandwiches
     A bug about sandwiches
     This cheese sandwich should show up

=== modified file 'lib/lp/bugs/doc/bugtask-package-bugcounts.txt'
--- lib/lp/bugs/doc/bugtask-package-bugcounts.txt	2011-06-28 15:04:29 +0000
+++ lib/lp/bugs/doc/bugtask-package-bugcounts.txt	2018-06-30 11:02:51 +0000
@@ -20,8 +20,8 @@
     ...     for key, value in sorted(package_count.items()):
     ...         if IDistributionSourcePackage.providedBy(value):
     ...             value = value.bugtargetdisplayname
-    ...         print "%s: %s" % (key, value)
-    ...     print # Blank line, to make output more readable.
+    ...         print("%s: %s" % (key, value))
+    ...     print() # Blank line, to make output more readable.
     >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
     >>> debian = getUtility(IDistributionSet).getByName('debian')
     >>> packages = [

=== modified file 'lib/lp/bugs/doc/bugtask-retrieval.txt'
--- lib/lp/bugs/doc/bugtask-retrieval.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/bugs/doc/bugtask-retrieval.txt	2018-06-30 11:02:51 +0000
@@ -92,7 +92,7 @@
     Traceback (most recent call last):
     ...
     TypeError: ...
-    >>> print task_set.getMultiple(['1; DROP TABLE person;'])
+    >>> print(task_set.getMultiple(['1; DROP TABLE person;']))
     Traceback (most recent call last):
     ...
     ValueError: ...

=== modified file 'lib/lp/bugs/doc/bugtask-search-old-urls.txt'
--- lib/lp/bugs/doc/bugtask-search-old-urls.txt	2014-11-29 01:33:59 +0000
+++ lib/lp/bugs/doc/bugtask-search-old-urls.txt	2018-06-30 11:02:51 +0000
@@ -39,13 +39,14 @@
     ...     )
     >>> query_string_rewritten = (
     ...     rewrite_old_bugtask_status_query_string(query_string))
-    >>> print query_string_rewritten.split('&')
-    ['freddy=krueger',
-     'field.status%3Alist=New',
-     'field.status%3Alist=Incomplete',
-     'field.status%3Alist=Invalid',
-     'field.status%3Alist=Fix+Committed',
-     'sid=nancy']
+    >>> for param in query_string_rewritten.split('&'):
+    ...     print(param)
+    freddy=krueger
+    field.status%3Alist=New
+    field.status%3Alist=Incomplete
+    field.status%3Alist=Invalid
+    field.status%3Alist=Fix+Committed
+    sid=nancy
 
 If the url does not contain any old statuses
 rewrite_old_bugtask_status_query_string returns the original query

=== modified file 'lib/lp/bugs/doc/bugtask-search.txt'
--- lib/lp/bugs/doc/bugtask-search.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/bugtask-search.txt	2018-06-30 11:02:51 +0000
@@ -46,7 +46,7 @@
 
     >>> found_bugtasks = firefox.searchTasks(text_search)
     >>> for bugtask in found_bugtasks:
-    ...     print "#%s" % bugtask.bug.id
+    ...     print("#%s" % bugtask.bug.id)
     #4
 
 === BugTaskSearchParams' parameters searchtext and fast_searchtext ===
@@ -62,7 +62,7 @@
     ...     user=None, searchtext=u'happens pretty often')
     >>> found_bugtasks = firefox.searchTasks(good_search)
     >>> for bugtask in found_bugtasks:
-    ...     print "#%s" % bugtask.bug.id
+    ...     print("#%s" % bugtask.bug.id)
     #4
 
 The unstemmed word "happens" does not yield any results when used
@@ -71,7 +71,7 @@
     >>> bad_search = BugTaskSearchParams(
     ...     user=None, fast_searchtext=u'happens')
     >>> found_bugtasks = firefox.searchTasks(bad_search)
-    >>> print found_bugtasks.count()
+    >>> print(found_bugtasks.count())
     0
 
 If the stem of "happens" is used, we get results.
@@ -80,7 +80,7 @@
     ...     user=None, fast_searchtext=u'happen')
     >>> found_bugtasks = firefox.searchTasks(good_search)
     >>> for bugtask in found_bugtasks:
-    ...     print "#%s" % bugtask.bug.id
+    ...     print("#%s" % bugtask.bug.id)
     #4
     #6
 
@@ -90,7 +90,7 @@
     ...     user=None, fast_searchtext=u'happen&pretti&often')
     >>> found_bugtasks = firefox.searchTasks(good_search)
     >>> for bugtask in found_bugtasks:
-    ...     print "#%s" % bugtask.bug.id
+    ...     print("#%s" % bugtask.bug.id)
     #4
 
 Passing invalid tsquery expressions as fast_searchtext raises an exception.
@@ -157,7 +157,7 @@
     ...     user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
     ...     hardware_product_id='0x0455', hardware_owner_is_bug_reporter=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     Firefox does not support SVG
     Blackhole Trash folder
 
@@ -170,7 +170,7 @@
     ...     user=None, hardware_bus=HWBus.PCI, hardware_vendor_id='0x10de',
     ...     hardware_owner_is_bug_reporter=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.title
+    ...     print(bugtask.bug.title)
     Firefox does not support SVG
     Thunderbird crashes
     another test bug
@@ -184,7 +184,7 @@
     ...     user=None, hardware_driver_name='ehci_hcd',
     ...     hardware_owner_is_bug_reporter=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.id, bugtask.bug.owner.displayname
+    ...     print(bugtask.bug.id, bugtask.bug.owner.displayname)
     1 Sample Person
     2 Sample Person
 
@@ -195,7 +195,7 @@
     ...     hardware_driver_package_name='linux-image-2.6.24-19-generic',
     ...     hardware_owner_is_bug_reporter=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.id, bugtask.bug.owner.displayname
+    ...     print(bugtask.bug.id, bugtask.bug.owner.displayname)
     1 Sample Person
     2 Sample Person
 
@@ -214,7 +214,7 @@
     ...     hardware_product_id='0x0455', hardware_driver_name='ehci_hcd',
     ...     hardware_owner_is_bug_reporter=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.id, bugtask.bug.owner.displayname
+    ...     print(bugtask.bug.id, bugtask.bug.owner.displayname)
     1 Sample Person
     2 Sample Person
 
@@ -239,7 +239,7 @@
     ...     hardware_product_id='0x0455',
     ...     hardware_owner_is_subscribed_to_bug=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.id, bugtask.bug.isSubscribed(sample_person)
+    ...     print(bugtask.bug.id, bugtask.bug.isSubscribed(sample_person))
     1 True
     9 True
 
@@ -252,7 +252,7 @@
     ...     hardware_product_id='0x0455',
     ...     hardware_owner_is_affected_by_bug=True)
     >>> for bugtask in ubuntu.searchTasks(search_params):
-    ...     print bugtask.bug.id, bugtask.bug.isUserAffected(sample_person)
+    ...     print(bugtask.bug.id, bugtask.bug.isUserAffected(sample_person))
     10 True
 
 Finally, we can search for who bugs which are directly linked to
@@ -323,7 +323,7 @@
     ...     orderby='-number_of_duplicates', user=None)
     >>> ubuntu_tasks = ubuntu.searchTasks(params)
     >>> for bugtask in ubuntu_tasks:
-    ...     print bugTaskInfo(bugtask)
+    ...     print(bugTaskInfo(bugtask))
     mozilla-firefox (Ubuntu) Firefox does not support SVG
     thunderbird (Ubuntu) Thunderbird crashes
     linux-source-2.6.15 (Ubuntu) another test bug
@@ -350,7 +350,7 @@
 
     >>> ubuntu_tasks = ubuntu.searchTasks(params)
     >>> for bugtask in ubuntu_tasks:
-    ...     print bugTaskInfo(bugtask)
+    ...     print(bugTaskInfo(bugtask))
     thunderbird (Ubuntu) Thunderbird crashes
     mozilla-firefox (Ubuntu) Firefox does not support SVG
     linux-source-2.6.15 (Ubuntu) another test bug
@@ -369,7 +369,7 @@
     >>> ubuntu_tasks = ubuntu.searchTasks(params)
     >>> for bugtask in ubuntu_tasks:
     ...     bug = bugtask.bug
-    ...     print '%s [%s comments]' % (bug.title, bug.message_count)
+    ...     print('%s [%s comments]' % (bug.title, bug.message_count))
     Blackhole Trash folder [3 comments]
     Firefox does not support SVG [2 comments]
     another test bug [2 comments]
@@ -392,7 +392,7 @@
     >>> ubuntu_tasks = ubuntu.searchTasks(params)
     >>> for bugtask in ubuntu_tasks:
     ...     bug = bugtask.bug
-    ...     print '%s [heat: %s]' % (bug.title, bug.heat)
+    ...     print('%s [heat: %s]' % (bug.title, bug.heat))
     Bug to be fixed in trunk [heat: 16]
     another test bug [heat: 10]
     Thunderbird crashes [heat: 9]
@@ -413,7 +413,7 @@
     ...     orderby='latest_patch_uploaded', user=None)
     >>> ubuntu_tasks = ubuntu.searchTasks(params)
     >>> for bugtask in ubuntu_tasks:
-    ...     print bugTaskInfo(bugtask)
+    ...     print(bugTaskInfo(bugtask))
     cdrkit (Ubuntu) Bug to be fixed in trunk
     Ubuntu Blackhole Trash folder
     linux-source-2.6.15 (Ubuntu) another test bug
@@ -432,7 +432,7 @@
     ...     orderby='latest_patch_uploaded', user=None)
     >>> ubuntu_tasks = ubuntu.searchTasks(params)
     >>> for bugtask in ubuntu_tasks:
-    ...     print bugTaskInfo(bugtask)
+    ...     print(bugTaskInfo(bugtask))
     Ubuntu Blackhole Trash folder
     linux-source-2.6.15 (Ubuntu) another test bug
     cdrkit (Ubuntu) Bug to be fixed in trunk

=== modified file 'lib/lp/bugs/doc/bugtask-status-changes.txt'
--- lib/lp/bugs/doc/bugtask-status-changes.txt	2012-09-07 21:00:46 +0000
+++ lib/lp/bugs/doc/bugtask-status-changes.txt	2018-06-30 11:02:51 +0000
@@ -20,7 +20,7 @@
     >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
     >>> ignored = login_person(owner)
     >>> bugtask.transitionToStatus(BugTaskStatus.WONTFIX, owner)
-    >>> print bugtask.status.title
+    >>> print(bugtask.status.title)
     Won't Fix
 
 Regular users of Launchpad cannot transition a bug task to any of

=== modified file 'lib/lp/bugs/doc/bugtask-status-workflow.txt'
--- lib/lp/bugs/doc/bugtask-status-workflow.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/bugs/doc/bugtask-status-workflow.txt	2018-06-30 11:02:51 +0000
@@ -27,9 +27,9 @@
 
     >>> ubuntu_firefox_task = new_bug.bugtasks[0]
 
-    >>> print ubuntu_firefox_task.distribution.name
+    >>> print(ubuntu_firefox_task.distribution.name)
     ubuntu
-    >>> print ubuntu_firefox_task.sourcepackagename.name
+    >>> print(ubuntu_firefox_task.sourcepackagename.name)
     mozilla-firefox
 
 Only its datecreated value will be set. All other dates will be None.
@@ -59,7 +59,7 @@
       ...
     ForbiddenAttribute: ...
 
-    >>> print ubuntu_firefox_task.status.title
+    >>> print(ubuntu_firefox_task.status.title)
     New
 
 Confirming the bug will set IBugTask.date_confirmed. As with all the
@@ -71,7 +71,7 @@
     >>> ubuntu_firefox_task.transitionToStatus(
     ...     BugTaskStatus.CONFIRMED, getUtility(ILaunchBag).user)
 
-    >>> print ubuntu_firefox_task.status.title
+    >>> print(ubuntu_firefox_task.status.title)
     Confirmed
     >>> ubuntu_firefox_task.date_confirmed
     datetime.datetime...
@@ -122,7 +122,7 @@
     >>> ubuntu_firefox_task.transitionToStatus(
     ...     BugTaskStatus.INPROGRESS, getUtility(ILaunchBag).user)
 
-    >>> print ubuntu_firefox_task.status.title
+    >>> print(ubuntu_firefox_task.status.title)
     In Progress
     >>> ubuntu_firefox_task.date_inprogress
     datetime.datetime...
@@ -148,7 +148,7 @@
 
 Marking the bug Triaged sets `date_triaged`.
 
-    >>> print ubuntu_firefox_task.date_triaged
+    >>> print(ubuntu_firefox_task.date_triaged)
     None
 
     >>> ubuntu_firefox_task.transitionToStatus(
@@ -162,7 +162,7 @@
     >>> ubuntu_firefox_task.transitionToStatus(
     ...     BugTaskStatus.NEW, getUtility(ILaunchBag).user)
 
-    >>> print ubuntu_firefox_task.date_triaged
+    >>> print(ubuntu_firefox_task.date_triaged)
     None
 
 If the status is changed from any unresolved status to any resolved
@@ -211,13 +211,13 @@
 Changing from one closed status to another does not change the
 date_closed.
 
-    >>> print ubuntu_firefox_task.status.title
+    >>> print(ubuntu_firefox_task.status.title)
     Fix Released
     >>> prev_date_closed = ubuntu_firefox_task.date_closed
     >>> ubuntu_firefox_task.transitionToStatus(
     ...     BugTaskStatus.INVALID, getUtility(ILaunchBag).user)
 
-    >>> print ubuntu_firefox_task.status.title
+    >>> print(ubuntu_firefox_task.status.title)
     Invalid
     >>> ubuntu_firefox_task.date_closed == prev_date_closed
     True
@@ -235,7 +235,7 @@
 
 We also record the date a task was marked Fix Commited.
 
-    >>> print ubuntu_firefox_task.date_fix_committed
+    >>> print(ubuntu_firefox_task.date_fix_committed)
     None
 
     >>> ubuntu_firefox_task.transitionToStatus(
@@ -249,12 +249,12 @@
     >>> ubuntu_firefox_task.transitionToStatus(
     ...     BugTaskStatus.INPROGRESS, getUtility(ILaunchBag).user)
 
-    >>> print ubuntu_firefox_task.date_fix_committed
+    >>> print(ubuntu_firefox_task.date_fix_committed)
     None
 
 We also record the date a task was marked Fix Released.
 
-    >>> print ubuntu_firefox_task.date_fix_released
+    >>> print(ubuntu_firefox_task.date_fix_released)
     None
 
     >>> ubuntu_firefox_task.transitionToStatus(
@@ -268,7 +268,7 @@
     >>> ubuntu_firefox_task.transitionToStatus(
     ...     BugTaskStatus.INPROGRESS, getUtility(ILaunchBag).user)
 
-    >>> print ubuntu_firefox_task.date_fix_committed
+    >>> print(ubuntu_firefox_task.date_fix_committed)
     None
 
 Lastly, IBugTask.date_assigned is set when a bugtask goes from being

=== modified file 'lib/lp/bugs/doc/bugtracker-person.txt'
--- lib/lp/bugs/doc/bugtracker-person.txt	2017-10-24 13:08:32 +0000
+++ lib/lp/bugs/doc/bugtracker-person.txt	2018-06-30 11:02:51 +0000
@@ -27,13 +27,13 @@
     >>> bugtracker_person = bugtracker.linkPersonToSelf(
     ...         'some-name-i-made-up', sample_person)
 
-    >>> print bugtracker_person.name
+    >>> print(bugtracker_person.name)
     some-name-i-made-up
 
-    >>> print bugtracker_person.person.name
+    >>> print(bugtracker_person.person.name)
     name12
 
-    >>> print bugtracker_person.bugtracker.name
+    >>> print(bugtracker_person.bugtracker.name)
     bugzilla-checkwatches
 
 A name can only be registered with a bugtracker once. Trying to link a
@@ -53,10 +53,10 @@
     >>> bugtracker_person = bugtracker.getLinkedPersonByName(
     ...     'some-name-i-made-up')
 
-    >>> print bugtracker_person.name
+    >>> print(bugtracker_person.name)
     some-name-i-made-up
 
-    >>> print bugtracker_person.person.name
+    >>> print(bugtracker_person.person.name)
     name12
 
 
@@ -73,7 +73,7 @@
     >>> from lp.registry.interfaces.person import (
     ...     PersonCreationRationale)
 
-    >>> print getUtility(IPersonSet).getByEmail('new.person@xxxxxxxxxxx')
+    >>> print(getUtility(IPersonSet).getByEmail('new.person@xxxxxxxxxxx'))
     None
 
     >>> new_person = bugtracker.ensurePersonForSelf(
@@ -81,7 +81,7 @@
     ...     rationale=PersonCreationRationale.BUGIMPORT,
     ...     creation_comment='whilst testing ensurePersonForSelf().')
 
-    >>> print new_person.displayname
+    >>> print(new_person.displayname)
     New Person
 
 There won't be a BugTrackerPerson record linking 'New Person' to the
@@ -90,7 +90,7 @@
 remote bugtracker.
 
     >>> bugtracker_person = bugtracker.getLinkedPersonByName('New Person')
-    >>> print bugtracker_person
+    >>> print(bugtracker_person)
     None
 
 Calling ensurePersonForSelf() with the same details will return the
@@ -102,10 +102,10 @@
     ...     rationale=PersonCreationRationale.BUGIMPORT,
     ...     creation_comment='whilst testing ensurePersonForSelf().')
 
-    >>> print other_person.name
+    >>> print(other_person.name)
     new-person
 
-    >>> print new_person.name
+    >>> print(new_person.name)
     new-person
 
 ensurePersonForSelf() can also handle remote users whose email
@@ -116,7 +116,7 @@
     ...     email=None, rationale=PersonCreationRationale.BUGIMPORT,
     ...     creation_comment='whilst testing ensurePersonForSelf().')
 
-    >>> print noemail_person.name
+    >>> print(noemail_person.name)
     no-email-person-bugzilla-checkwatches
 
 A BugTrackerPerson record will have been created to map
@@ -139,15 +139,15 @@
     ...     email=None, rationale=PersonCreationRationale.BUGIMPORT,
     ...     creation_comment='whilst testing.')
 
-    >>> print new_person.name
+    >>> print(new_person.name)
     noemail-bugzilla-checkwatches
 
     >>> bugtracker_person = bugtracker.getLinkedPersonByName('noemail')
 
-    >>> print bugtracker_person.bugtracker.name
+    >>> print(bugtracker_person.bugtracker.name)
     bugzilla-checkwatches
 
-    >>> print bugtracker_person.person.name
+    >>> print(bugtracker_person.person.name)
     noemail-bugzilla-checkwatches
 
     >>> transaction.commit()
@@ -178,9 +178,9 @@
     ...     'noemail', None, PersonCreationRationale.BUGIMPORT,
     ...     'while testing, again')
 
-    >>> print original_bugtracker_person.person.name
+    >>> print(original_bugtracker_person.person.name)
     noemail-bugzilla-checkwatches
 
-    >>> print new_person.name
+    >>> print(new_person.name)
     noemail-bugzilla-checkwatches-1
 

=== modified file 'lib/lp/bugs/doc/bugtracker-tokens.txt'
--- lib/lp/bugs/doc/bugtracker-tokens.txt	2012-12-20 13:43:48 +0000
+++ lib/lp/bugs/doc/bugtracker-tokens.txt	2018-06-30 11:02:51 +0000
@@ -27,7 +27,7 @@
     ...     token, request=test_request, rootsite='mainsite',
     ...     view_name='+bugtracker-handshake')
 
-    >>> print token_url
+    >>> print(token_url)
     http://launchpad.dev/token/.../+bugtracker-handshake
 
 Visiting the token's +bugtracker-handshake URL will result in an HTTP
@@ -39,13 +39,13 @@
     >>> view()
     'Handshake token validated.'
 
-    >>> print test_request.response.getStatus()
+    >>> print(test_request.response.getStatus())
     200
 
 The token has now been consumed.
 
     >>> token = getUtility(ILoginTokenSet)[token_string]
-    >>> print token.date_consumed is not None
+    >>> print(token.date_consumed is not None)
     True
 
 If we try to access the +bugtracker-handshake URL again, we will receive
@@ -56,7 +56,7 @@
     >>> view()
     'Token has already been used or is invalid.'
 
-    >>> print test_request.response.getStatus()
+    >>> print(test_request.response.getStatus())
     410
 
 Only POST requests are valid when accessing a +bugtracker-handshake URL.
@@ -72,12 +72,12 @@
     >>> view()
     'Only POST requests are accepted for bugtracker handshakes.'
 
-    >>> print test_request.response.getStatus()
+    >>> print(test_request.response.getStatus())
     405
 
 However, since the request was invalid the token will not have been
 consumed.
 
     >>> token = getUtility(ILoginTokenSet)[token_string]
-    >>> print token.date_consumed
+    >>> print(token.date_consumed)
     None

=== modified file 'lib/lp/bugs/doc/bugtracker.txt'
--- lib/lp/bugs/doc/bugtracker.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/doc/bugtracker.txt	2018-06-30 11:02:51 +0000
@@ -20,8 +20,8 @@
     ...         key=lambda watch: (watch.remotebug, watch.bug.id))
     ...
     ...     for bug_watch in watches:
-    ...         print "Remote bug: %s LP bug: %s" % (
-    ...             bug_watch.remotebug, bug_watch.bug.id)
+    ...         print("Remote bug: %s LP bug: %s" % (
+    ...             bug_watch.remotebug, bug_watch.bug.id))
 
 We must be an admin to modify bug watches later on.
 
@@ -32,25 +32,25 @@
 next_check time is in the past.
 
     >>> bug_watches = mozilla_bugzilla.watches
-    >>> print bug_watches.count()
+    >>> print(bug_watches.count())
     4
 
-    >>> print bug_watches[0].remotebug, bug_watches[0].bug.id
+    >>> print(bug_watches[0].remotebug, bug_watches[0].bug.id)
     2000 1
     >>> bug_watches[0].next_check = now - timedelta(hours=1)
 
-    >>> print bug_watches[1].remotebug, bug_watches[1].bug.id
+    >>> print(bug_watches[1].remotebug, bug_watches[1].bug.id)
     123543 1
     >>> bug_watches[1].lastchecked = now - timedelta(hours=12)
 
 Note that bugtracker.watches may produce multiple watches for the same
 remote bug.
 
-    >>> print bug_watches[2].remotebug, bug_watches[2].bug.id
+    >>> print(bug_watches[2].remotebug, bug_watches[2].bug.id)
     42 1
     >>> bug_watches[2].next_check = now - timedelta(hours=36)
 
-    >>> print bug_watches[3].remotebug, bug_watches[3].bug.id
+    >>> print(bug_watches[3].remotebug, bug_watches[3].bug.id)
     42 2
     >>> bug_watches[3].next_check = now - timedelta(days=1)
 
@@ -74,7 +74,7 @@
     >>> message = factory.makeMessage(
     ...     'Unpushed comment', '... is unpushed')
 
-    >>> print bug_watches[1].remotebug
+    >>> print(bug_watches[1].remotebug)
     123543
 
     >>> bug_message = bug_watches[1].addComment(None, message)
@@ -268,7 +268,7 @@
     >>> trackers = list(bugtracker_set)
     >>> pillars = bugtracker_set.getPillarsForBugtrackers(trackers)
     >>> for t in pillars:
-    ...     print t.name, [p.name for p in pillars[t]]
+    ...     print(t.name, [p.name for p in pillars[t]])
     gnome-bugzilla [u'gnome-terminal', u'gnome']
 
 
@@ -280,9 +280,9 @@
 
     >>> def print_bug_messages(bug_messages):
     ...     for bug_message in bug_messages:
-    ...         print '* bug: %d' % bug_message.bug.id
-    ...         print '- remote bug: %s' % bug_message.bugwatch.remotebug
-    ...         print '- message subject: %s' % bug_message.message.subject
+    ...         print('* bug: %d' % bug_message.bug.id)
+    ...         print('- remote bug: %s' % bug_message.bugwatch.remotebug)
+    ...         print('- message subject: %s' % bug_message.message.subject)
 
 The Mozilla Bugzilla has only one imported bug message.
 
@@ -330,7 +330,7 @@
 
     >>> def print_links(links_dict):
     ...     for key in sorted(links_dict):
-    ...         print "%s: %s" % (key, links_dict[key])
+    ...         print("%s: %s" % (key, links_dict[key]))
 
     >>> links = mozilla_bugzilla.getBugFilingAndSearchLinks(
     ...     remote_product='testproduct', summary="Foo", description="Bar")
@@ -506,13 +506,13 @@
 Our example Trac bug tracker's `multi_product` property will be False,
 since it only tracks one product at a time.
 
-    >>> print example_trac.multi_product
+    >>> print(example_trac.multi_product)
     False
 
 However, Bugzilla instances require remote products in order to be able
 to return a bug filing URL.
 
-    >>> print mozilla_bugzilla.multi_product
+    >>> print(mozilla_bugzilla.multi_product)
     True
 
 There is a test in database/tests/test_bugtracker.py that checks that

=== modified file 'lib/lp/bugs/doc/bugwatch.txt'
--- lib/lp/bugs/doc/bugwatch.txt	2018-06-22 21:56:16 +0000
+++ lib/lp/bugs/doc/bugwatch.txt	2018-06-30 11:02:51 +0000
@@ -184,7 +184,7 @@
     ...     (bug_watch.bugtracker.bugtrackertype, bug_watch.remotebug)
     ...     for bug_watch in bug_watches]
     >>> for bugtracker_type, remotebug in sorted(bugs_and_types):
-    ...     print "%s: %s" % (bugtracker_type.name, remotebug)
+    ...     print("%s: %s" % (bugtracker_type.name, remotebug))
     BUGZILLA: 42
     DEBBUGS: 42
     ROUNDUP: 42
@@ -239,13 +239,13 @@
     >>> bug_watch_updater_user = getUtility(ILaunchBag).user
     >>> bug_one = getUtility(IBugSet).get(1)
     >>> bug_one.expireNotifications()
-    >>> print len(bug_one.bugtasks)
+    >>> print(len(bug_one.bugtasks))
     3
     >>> debian_task = bug_one.bugtasks[2]
-    >>> print debian_task.bugtargetdisplayname
+    >>> print(debian_task.bugtargetdisplayname)
     mozilla-firefox (Debian)
 
-    >>> print debian_task.status.title
+    >>> print(debian_task.status.title)
     Confirmed
 
     >>> debian_bugwatch = debian_task.bugwatch
@@ -258,11 +258,11 @@
     >>> def print_bugtask_modified(bugtask, event):
     ...     old_bugtask = event.object_before_modification
     ...     if bugtask.status != old_bugtask.status:
-    ...         print "%s => %s" % (old_bugtask.status.title,
-    ...             bugtask.status.title)
+    ...         print("%s => %s" % (old_bugtask.status.title,
+    ...             bugtask.status.title))
     ...     if bugtask.importance != old_bugtask.importance:
-    ...         print "%s => %s" % (old_bugtask.importance.title,
-    ...             bugtask.importance.title)
+    ...         print("%s => %s" % (old_bugtask.importance.title,
+    ...             bugtask.importance.title))
     >>> from lp.testing.event import TestEventListener
     >>> from lazr.lifecycle.interfaces import IObjectModifiedEvent
     >>> from lp.bugs.interfaces.bugtask import IBugTask
@@ -288,7 +288,7 @@
 
     >>> debian_bugwatch.remotestatus == old_remotestatus
     True
-    >>> print debian_task.status.title
+    >>> print(debian_task.status.title)
     New
 
 If only the remote status is changed, not the bugtask's status, no
@@ -299,7 +299,7 @@
 
     >>> debian_bugwatch.remotestatus
     u'some status'
-    >>> print debian_task.status.title
+    >>> print(debian_task.status.title)
     New
 
 The lastchanged was updated, though.
@@ -322,10 +322,10 @@
     ...     BugNotification.selectBy(date_emailed=None, orderBy='id'))
 
     >>> for bug_notification in unsent_notifications:
-    ...     print "Bug %s changed by %s:" % (
+    ...     print("Bug %s changed by %s:" % (
     ...         bug_notification.bug.id,
-    ...         bug_notification.message.owner.displayname)
-    ...     print bug_notification.message.text_contents
+    ...         bug_notification.message.owner.displayname))
+    ...     print(bug_notification.message.text_contents)
     Bug 1 changed by Bug Watch Updater:
     ** Changed in: mozilla-firefox (Debian)
            Status: Confirmed => New
@@ -356,7 +356,7 @@
     >>> debian_bugwatch.remote_importance == old_remote_importance
     True
 
-    >>> print debian_task.importance.title
+    >>> print(debian_task.importance.title)
     Critical
 
 If only the remote importance is changed, not the bugtask's importance,
@@ -368,7 +368,7 @@
 
     >>> debian_bugwatch.remote_importance
     u'some importance'
-    >>> print debian_task.importance.title
+    >>> print(debian_task.importance.title)
     Critical
 
 The `lastchanged` field was updated, though.
@@ -380,10 +380,10 @@
 manner:
 
     >>> for bug_notification in unsent_notifications:
-    ...     print "Bug %s changed by %s:" % (
+    ...     print("Bug %s changed by %s:" % (
     ...         bug_notification.bug.id,
-    ...         bug_notification.message.owner.displayname)
-    ...     print bug_notification.message.text_contents
+    ...         bug_notification.message.owner.displayname))
+    ...     print(bug_notification.message.text_contents)
     Bug 1 changed by Bug Watch Updater:
     ** Changed in: mozilla-firefox (Debian)
            Status: Confirmed => New

=== modified file 'lib/lp/bugs/doc/checkwatches-batching.txt'
--- lib/lp/bugs/doc/checkwatches-batching.txt	2016-02-05 16:51:12 +0000
+++ lib/lp/bugs/doc/checkwatches-batching.txt	2018-06-30 11:02:51 +0000
@@ -74,8 +74,8 @@
     >>> class QueryableRemoteSystem:
     ...     sync_comments = False
     ...     def getModifiedRemoteBugs(self, remote_bug_ids, timestamp):
-    ...         print "getModifiedRemoteBugs(%r, %r)" % (
-    ...             remote_bug_ids, timestamp)
+    ...         print("getModifiedRemoteBugs(%r, %r)" % (
+    ...             remote_bug_ids, timestamp))
     ...         # Return every *other* bug ID for demo purposes.
     ...         return remote_bug_ids[::2]
 

=== modified file 'lib/lp/bugs/doc/checkwatches-cli-switches.txt'
--- lib/lp/bugs/doc/checkwatches-cli-switches.txt	2012-01-20 15:42:44 +0000
+++ lib/lp/bugs/doc/checkwatches-cli-switches.txt	2018-06-30 11:02:51 +0000
@@ -121,7 +121,7 @@
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
     ...     stderr=subprocess.PIPE)
     >>> (out, err) = process.communicate()
-    >>> print out
+    >>> print(out)
     Usage: checkwatches.py [options]
     <BLANKLINE>
     Options:

=== modified file 'lib/lp/bugs/doc/checkwatches.txt'
--- lib/lp/bugs/doc/checkwatches.txt	2018-06-22 21:56:16 +0000
+++ lib/lp/bugs/doc/checkwatches.txt	2018-06-30 11:02:51 +0000
@@ -223,8 +223,8 @@
 since that's the error that was raised during the update.
 
     >>> for watch in savannah.watches:
-    ...     print "%s, %s" % (
-    ...         watch.lastchecked is not None, watch.last_error_type.title)
+    ...     print("%s, %s" % (
+    ...         watch.lastchecked is not None, watch.last_error_type.title))
     True, Unsupported Bugtracker
 
 If a bug tracker doesn't have any watches to update, forceUpdateAll()
@@ -331,22 +331,22 @@
     ...         return BugTaskImportance.UNKNOWN
     ...
     ...     def getCommentIds(self, bug_watch):
-    ...         print "getCommentIds() called"
+    ...         print("getCommentIds() called")
     ...         return []
     ...
     ...     def fetchComments(self, bug_watch, comment_ids):
     ...         return []
     ...
     ...     def addRemoteComment(self, bug_watch, comment):
-    ...         print "addRemoteComment() called."
+    ...         print("addRemoteComment() called.")
     ...         return 0
     ...
     ...     def getLaunchpadBugId(self, bug_id):
-    ...         print "getLaunchpadBugId() called"
+    ...         print("getLaunchpadBugId() called")
     ...         return None
     ...
     ...     def setLaunchpadBugId(self, bug_id, lp_bug_id, lp_bug_url):
-    ...         print "setLaunchpadBugId() called"
+    ...         print("setLaunchpadBugId() called")
 
 We'll generate a bug watch with which to test this. The bug watch must
 be associated with at least one bug task to enable syncing.

=== modified file 'lib/lp/bugs/doc/cve-update.txt'
--- lib/lp/bugs/doc/cve-update.txt	2016-08-16 15:27:19 +0000
+++ lib/lp/bugs/doc/cve-update.txt	2018-06-30 11:02:51 +0000
@@ -25,7 +25,7 @@
 entries are in the database.
 
     >>> from lp.bugs.model.cve import Cve
-    >>> print Cve.select().count()
+    >>> print(Cve.select().count())
     10
 
     >>> script = os.path.join(config.root, 'cronscripts', 'update-cve.py')
@@ -51,7 +51,7 @@
     ...         shutil.rmtree(tempdir)
 
     >>> output, empty = update_from_file(os.path.join(base, 'cvedb_init.xml'))
-    >>> print output
+    >>> print(output)
     INFO    Creating lockfile: /var/lock/launchpad-updatecve.lock
     ...
     INFO    CVE-1999-0002 created
@@ -81,7 +81,7 @@
 And let's make sure we got the right number of CVE entries.
 
     >>> transaction.commit()
-    >>> print Cve.select().count()
+    >>> print(Cve.select().count())
     18
 
 We will make a note of the CVE modification time of 1999-0002. When we
@@ -94,14 +94,14 @@
 And while we are here, make a note of the number of references for that CVE
 entry.
 
-    >>> print c.references.count()
+    >>> print(c.references.count())
     6
 
 Now, let's run an import of the update db.
 
     >>> output, empty = update_from_file(
     ...     os.path.join(base, 'cvedb_update.xml'))
-    >>> print output
+    >>> print(output)
     INFO    Creating lockfile: /var/lock/launchpad-updatecve.lock
     ...
     INFO    Creating new CERT reference for 1999-0002
@@ -137,16 +137,16 @@
 Let's make sure we got the new CVE's.
 
     >>> transaction.commit()
-    >>> print Cve.select().count()
+    >>> print(Cve.select().count())
     21
 
 And let's make sure the modification time of 2005-2734 was updated, as were
 the number of comments.
 
     >>> c.sync()
-    >>> print mod_time < c.datemodified
+    >>> print(mod_time < c.datemodified)
     True
-    >>> print c.references.count()
+    >>> print(c.references.count())
     0
 
 

=== modified file 'lib/lp/bugs/doc/cve.txt'
--- lib/lp/bugs/doc/cve.txt	2017-05-31 17:31:58 +0000
+++ lib/lp/bugs/doc/cve.txt	2018-06-30 11:02:51 +0000
@@ -70,7 +70,7 @@
     >>> from lp.bugs.model.bug import Bug
     >>> b = Bug.get(1)
     >>> for c in b.cves:
-    ...     print c.displayname
+    ...     print(c.displayname)
     CVE-1999-8979
 
 Let's add the new CVE:

=== modified file 'lib/lp/bugs/doc/displaying-bugs-and-tasks.txt'
--- lib/lp/bugs/doc/displaying-bugs-and-tasks.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/bugs/doc/displaying-bugs-and-tasks.txt	2018-06-30 11:02:51 +0000
@@ -68,10 +68,10 @@
   >>> from lp.bugs.interfaces.bug import IBugSet
   >>> bug1 = getUtility(IBugSet).get(1)
   >>> upstream_task = bug1.bugtasks[0]
-  >>> print upstream_task.product.name
+  >>> print(upstream_task.product.name)
   firefox
   >>> ubuntu_task = bug1.bugtasks[1]
-  >>> print ubuntu_task.distribution.name
+  >>> print(ubuntu_task.distribution.name)
   ubuntu
 
 So the logo for an upstream bug task shows the project icon:

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bug-imports.txt'
--- lib/lp/bugs/doc/externalbugtracker-bug-imports.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bug-imports.txt	2018-06-30 11:02:51 +0000
@@ -37,7 +37,7 @@
 if they don't exist.
 
     >>> from lp.registry.interfaces.person import IPersonSet
-    >>> print getUtility(IPersonSet).getByEmail('joe.bloggs@xxxxxxxxxxx')
+    >>> print(getUtility(IPersonSet).getByEmail('joe.bloggs@xxxxxxxxxxx'))
     None
 
 The method that imports a bug is importBug(). In addition to the
@@ -82,7 +82,7 @@
     >>> getUtility(IPersonSet).getByEmail(
     ...     'joe.bloggs@xxxxxxxxxxx', filter_status=False) is not None
     True
-    >>> print bug.owner.displayname
+    >>> print(bug.owner.displayname)
     Joe Bloggs
 
 Since they didn't have a Launchpad account before, they don't have a
@@ -94,9 +94,9 @@
     >>> reporter_email_addresses = getUtility(IEmailAddressSet).getByPerson(
     ...     bug.owner)
     >>> for email_address in reporter_email_addresses:
-    ...     print "%s: %s" % (email_address.email, email_address.status.name)
+    ...     print("%s: %s" % (email_address.email, email_address.status.name))
     joe.bloggs@xxxxxxxxxxx: NEW
-    >>> print bug.owner.preferredemail
+    >>> print(bug.owner.preferredemail)
     None
 
     >>> bug.owner.creation_rationale.name
@@ -136,7 +136,7 @@
     >>> external_bugtracker._bugs['5'] = {
     ...     'package': 'no-such-package',
     ...     'reporter': ("Joe Bloggs", "joe.bloggs@xxxxxxxxxxx")}
-    >>> print debian.getSourcePackage('no-such-package')
+    >>> print(debian.getSourcePackage('no-such-package'))
     None
     >>> bug = bug_watch_updater.importBug(
     ...     external_bugtracker, bugtracker, debian, '5')
@@ -146,7 +146,7 @@
     >>> [added_task] = bug.bugtasks
     >>> added_task.distribution.name
     u'debian'
-    >>> print added_task.sourcepackagename
+    >>> print(added_task.sourcepackagename)
     None
 
 
@@ -160,7 +160,7 @@
 
     >>> added_task.status.name
     'NEW'
-    >>> print added_task.bugwatch.lastchecked
+    >>> print(added_task.bugwatch.lastchecked)
     None
 
 

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2018-06-23 00:51:17 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-api.txt	2018-06-30 11:02:51 +0000
@@ -146,10 +146,10 @@
 
     >>> def print_bugs(bugs):
     ...     for bug in sorted(bugs):
-    ...         print "Bug %s:" % bug
+    ...         print("Bug %s:" % bug)
     ...         for key in sorted(bugs[bug]):
-    ...             print "    %s: %s" % (key, bugs[bug][key])
-    ...         print "\n"
+    ...             print("    %s: %s" % (key, bugs[bug][key]))
+    ...         print("\n")
 
     >>> print_bugs(bugzilla._bugs)
     Bug 1:
@@ -242,7 +242,7 @@
 the alias and the bug's actual ID.
 
     >>> for alias, bug_id in sorted(bugzilla._bug_aliases.items()):
-    ...     print "%s: %s" % (alias, bug_id)
+    ...     print("%s: %s" % (alias, bug_id))
     bad-diodes: 3
     bug-three: 3
     bug-two: 2
@@ -274,7 +274,7 @@
     >>> no_alias_bugzilla.initializeRemoteBugDB([1])
     CALLED Bug.get({'ids': [1], 'permissive': True})
 
-    >>> print len(no_alias_bugzilla._bug_aliases)
+    >>> print(len(no_alias_bugzilla._bug_aliases))
     0
 
 
@@ -287,19 +287,19 @@
     >>> test_transport.print_method_calls = False
     >>> bugzilla.initializeRemoteBugDB([1, 2])
 
-    >>> print bugzilla.getRemoteStatus(1)
+    >>> print(bugzilla.getRemoteStatus(1))
     RESOLVED FIXED
 
-    >>> print bugzilla.getRemoteStatus(2)
+    >>> print(bugzilla.getRemoteStatus(2))
     NEW
 
 Similarly, BugzillaAPI.getRemoteStatus() returns the remote priority and
 severity as a string.
 
-    >>> print bugzilla.getRemoteImportance(1)
+    >>> print(bugzilla.getRemoteImportance(1))
     P1 normal
 
-    >>> print bugzilla.getRemoteImportance(2)
+    >>> print(bugzilla.getRemoteImportance(2))
     P1 high
 
 If a bug can't be found a BugNotFound error will be raised.
@@ -344,7 +344,7 @@
     CALLED Bug.search({'id': [1, 2],
         'last_change_time': <DateTime ...'20080611T09:00:00' at...>})
 
-    >>> print bug_ids
+    >>> print(bug_ids)
     ['2']
 
 If we alter the changed_since date to move it back by a day, we'll get
@@ -355,7 +355,7 @@
     CALLED Bug.search({'id': [1, 2],
         'last_change_time': <DateTime ...'20080610T09:00:00' at...>})
 
-    >>> print bug_ids
+    >>> print(bug_ids)
     ['1', '2']
 
 Bugzilla's Bug.search() method returns all the data for each bug it
@@ -363,10 +363,10 @@
 BugzillaAPI instance's bugs dict.
 
     >>> for bug in sorted(bugzilla._bugs):
-    ...     print "Bug %s:" % bug
+    ...     print("Bug %s:" % bug)
     ...     for key in sorted(bugzilla._bugs[bug]):
-    ...         print "    %s: %s" % (key, bugzilla._bugs[bug][key])
-    ...     print "\n"
+    ...         print("    %s: %s" % (key, bugzilla._bugs[bug][key]))
+    ...     print("\n")
     Bug 1:
         alias:
         assigned_to: test@xxxxxxxxxxxxx...
@@ -459,7 +459,7 @@
     >>> bug_comment_ids = bugzilla.getCommentIds(bug_watch.remotebug)
     CALLED Bug.comments({'ids': [1], 'include_fields': ['id']})
 
-    >>> print sorted(bug_comment_ids)
+    >>> print(sorted(bug_comment_ids))
     ['1', '3']
 
 getCommentIds() can only be called if initializeRemoteBugDB() has been
@@ -489,10 +489,10 @@
 
     >>> comments = bugzilla._bugs[1]['comments']
     >>> for comment_id in sorted(comments):
-    ...     print "Comment %s:" % comment_id
+    ...     print("Comment %s:" % comment_id)
     ...     comment = comments[comment_id]
     ...     for key in sorted(comment):
-    ...         print "    %s: %s" % (key, comment[key])
+    ...         print("    %s: %s" % (key, comment[key]))
     Comment 1:
         author: trillian
         bug_id: 1
@@ -519,10 +519,10 @@
 
     >>> comments = bugzilla._bugs[1]['comments']
     >>> for comment_id in sorted(comments):
-    ...     print "Comment %s:" % comment_id
+    ...     print("Comment %s:" % comment_id)
     ...     comment = comments[comment_id]
     ...     for key in sorted(comment):
-    ...         print "    %s: %s" % (key, comment[key])
+    ...         print("    %s: %s" % (key, comment[key]))
     Comment 1:
         author: trillian
         bug_id: 1
@@ -543,7 +543,7 @@
 
     >>> bugzilla.fetchComments(remote_bug, ['2', '4', '5', '6'])
     >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '4')
-    >>> print displayname, email
+    >>> print(displayname, email)
     Ford Prefect ford.prefect@xxxxxxxx
 
 getPosterForComment() handles situations in which only an email address
@@ -552,7 +552,7 @@
 name will be generated for the user from their email address.
 
     >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '5')
-    >>> print displayname, email
+    >>> print(displayname, email)
     None arthur.dent@xxxxxxxxxxxxxxxxx
 
 getPosterForComment() will also return displayname, email tuples in
@@ -561,14 +561,14 @@
 None.
 
     >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '2')
-    >>> print displayname, email
+    >>> print(displayname, email)
     trillian None
 
 Bugzilla 4.0 renamed the 'author' field to 'creator', but kept the old field
 for compatibility.  Bugzilla 5.0 dropped the compatibility field.
 
     >>> displayname, email = bugzilla.getPosterForComment(remote_bug, '6')
-    >>> print displayname, email
+    >>> print(displayname, email)
     Slartibartfast slarti@xxxxxxxxxxxxxxxxxxxxx
 
 
@@ -583,19 +583,19 @@
 
     >>> message = bugzilla.getMessageForComment(
     ...     bug_watch_two.remotebug, '2', sample_person)
-    >>> print message.text_contents
+    >>> print(message.text_contents)
     Bring the passengers to the bridge please Marvin.
 
-    >>> print message.owner.displayname
+    >>> print(message.owner.displayname)
     Sample Person
 
 The datecreated attribute of the Message will be the same as the 'time'
 field on the imported comment.
 
-    >>> print bugzilla._bugs[2]['comments'][2]['time']
+    >>> print(bugzilla._bugs[2]['comments'][2]['time'])
     2008-06-16 13:08:08
 
-    >>> print message.datecreated
+    >>> print(message.datecreated)
     2008-06-16 13:08:08+00:00
 
 
@@ -646,7 +646,7 @@
 The comment will be stored on the remote server with the other comments.
 
     >>> bugzilla.xmlrpc_transport.print_method_calls = False
-    >>> print sorted(bugzilla.getCommentIds(bug_watch.remotebug))
+    >>> print(sorted(bugzilla.getCommentIds(bug_watch.remotebug)))
     ['1', '3', '7']
 
     >>> remote_bug = bug_watch.remotebug
@@ -655,7 +655,7 @@
     >>> bugzilla.fetchComments(remote_bug, ['7'])
     >>> message = bugzilla.getMessageForComment(
     ...     bug_watch.remotebug, '7', sample_person)
-    >>> print message.text_contents
+    >>> print(message.text_contents)
     This is a new remote comment.
     <BLANKLINE>
 

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2018-06-23 00:51:17 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-lp-plugin.txt	2018-06-30 11:02:51 +0000
@@ -123,7 +123,7 @@
     Successfully validated the token.
     CALLED Test.login_required()
 
-    >>> print return_value
+    >>> print(return_value)
     Wonderful, you've logged in! Aren't you a clever biped?
 
     >>> test_transport.print_method_calls = False
@@ -199,10 +199,10 @@
 
     >>> def print_bugs(bugs):
     ...     for bug in sorted(bugs):
-    ...         print "Bug %s:" % bug
+    ...         print("Bug %s:" % bug)
     ...         for key in sorted(bugs[bug]):
-    ...             print "    %s: %s" % (key, bugs[bug][key])
-    ...         print "\n"
+    ...             print("    %s: %s" % (key, bugs[bug][key]))
+    ...         print("\n")
 
     >>> print_bugs(bugzilla._bugs)
     Bug 1:
@@ -265,7 +265,7 @@
         'ids': [1, 2],
         'permissive': True})
 
-    >>> print bug_ids
+    >>> print(bug_ids)
     [2]
 
 If we alter the changed_since date to move it back by a day, we'll get
@@ -278,7 +278,7 @@
         'ids': [1, 2],
         'permissive': True})
 
-    >>> print bug_ids
+    >>> print(bug_ids)
     [1, 2]
 
 Bugzilla's Launchpad.get_bugs() method returns all the data for each
@@ -286,10 +286,10 @@
 BugzillaLPPlugin instance's bugs dict.
 
     >>> for bug in sorted(bugzilla._bugs):
-    ...     print "Bug %s:" % bug
+    ...     print("Bug %s:" % bug)
     ...     for key in sorted(bugzilla._bugs[bug]):
-    ...         print "    %s: %s" % (key, bugzilla._bugs[bug][key])
-    ...     print "\n"
+    ...         print("    %s: %s" % (key, bugzilla._bugs[bug][key]))
+    ...     print("\n")
     Bug 1:
         alias:
         assigned_to: test@xxxxxxxxxxxxx...
@@ -373,7 +373,7 @@
     >>> bug_comment_ids = bugzilla.getCommentIds(remote_bug)
     CALLED Launchpad.comments({'bug_ids': [1], 'include_fields': ['id']})
 
-    >>> print sorted(bug_comment_ids)
+    >>> print(sorted(bug_comment_ids))
     ['1', '3']
 
 getCommentIds() can only be called if initializeRemoteBugDB() has been
@@ -406,10 +406,10 @@
 
     >>> comments = bugzilla._bugs[1]['comments']
     >>> for comment_id in sorted(comments):
-    ...     print "Comment %s:" % comment_id
+    ...     print("Comment %s:" % comment_id)
     ...     comment = comments[comment_id]
     ...     for key in sorted(comment):
-    ...         print "    %s: %s" % (key, comment[key])
+    ...         print("    %s: %s" % (key, comment[key]))
     Comment 1:
         author: trillian
         id: 1
@@ -468,7 +468,7 @@
     >>> transaction.commit()
 
     >>> bugzilla.xmlrpc_transport.print_method_calls = False
-    >>> print sorted(bugzilla.getCommentIds(remote_bug))
+    >>> print(sorted(bugzilla.getCommentIds(remote_bug)))
     ['1', '3', '7']
 
     >>> transaction.commit()
@@ -476,7 +476,7 @@
     >>> bugzilla.fetchComments(remote_bug, ['7'])
     >>> message = bugzilla.getMessageForComment(
     ...     remote_bug, '7', sample_person)
-    >>> print message.text_contents
+    >>> print(message.text_contents)
     This is a new remote comment.
     <BLANKLINE>
 
@@ -501,7 +501,7 @@
 If there is no bug currently linked to the remote bug,
 getLaunchpadBugId() will return None.
 
-    >>> print launchpad_bug_id
+    >>> print(launchpad_bug_id)
     None
 
 We'll set the launchpad_id for the remote bug so that we can retrieve
@@ -513,7 +513,7 @@
 set.
 
     >>> launchpad_bug_id = bugzilla.getLaunchpadBugId(1)
-    >>> print launchpad_bug_id
+    >>> print(launchpad_bug_id)
     42
 
 setLaunchpadBugId() is used to set the Launchpad bug ID for a given
@@ -542,7 +542,7 @@
     CALLED Launchpad.get_bugs({'ids': [1], 'permissive': True})
 
     >>> launchpad_bug_id = bugzilla.getLaunchpadBugId(1)
-    >>> print launchpad_bug_id
+    >>> print(launchpad_bug_id)
     10
 
 
@@ -607,6 +607,6 @@
     CALLED Launchpad.get_bugs({'ids': [1, 2], 'permissive': True})
 
     >>> for bug_id in sorted(product_mappings):
-    ...     print "%s: %s" % (bug_id, product_mappings[bug_id])
+    ...     print("%s: %s" % (bug_id, product_mappings[bug_id]))
     1: Marvin
     2: HeartOfGold

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt	2018-06-23 00:51:17 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt	2018-06-30 11:02:51 +0000
@@ -26,8 +26,8 @@
     ...     issuezilla._probe_version()
     (2, 11)
     >>> for bug_watch in mozilla_bugzilla.watches:
-    ...     print "%s: %s %s" % (bug_watch.remotebug,
-    ...         bug_watch.remotestatus, bug_watch.remote_importance)
+    ...     print("%s: %s %s" % (bug_watch.remotebug,
+    ...         bug_watch.remotestatus, bug_watch.remote_importance))
     2000:
     123543:
     42: FUBAR BAZBAZ
@@ -42,8 +42,8 @@
     (local bugs: 1, 2).
 
     >>> for bug_watch in mozilla_bugzilla.watches:
-    ...     print "%s: %s %s" % (bug_watch.remotebug,
-    ...         bug_watch.remotestatus, bug_watch.remote_importance)
+    ...     print("%s: %s %s" % (bug_watch.remotebug,
+    ...         bug_watch.remotestatus, bug_watch.remote_importance))
     2000: RESOLVED FIXED LOW
     123543: ASSIGNED HIGH
     42: FUBAR BAZBAZ
@@ -80,10 +80,10 @@
     >>> with old_bugzilla.responses():
     ...     old_bugzilla.initializeRemoteBugDB(remote_bugs)
     >>> for remote_bug in remote_bugs:
-    ...     print "%s: %s %s" % (
+    ...     print("%s: %s %s" % (
     ...         remote_bug,
     ...         old_bugzilla.getRemoteStatus(remote_bug),
-    ...         old_bugzilla.getRemoteImportance(remote_bug))
+    ...         old_bugzilla.getRemoteImportance(remote_bug)))
     42: RESOLVED FIXED LOW BLOCKER
     123543: ASSIGNED HIGH BLOCKER
 
@@ -104,8 +104,8 @@
   a) The bug status tag is <bz:status> and not <bz:bug_status>
 
     >>> bug_item_file = weird_bugzilla._readBugItemFile()
-    >>> "<bz:status>" in bug_item_file
-    True
+    >>> print(bug_item_file)
+    <li>...<bz:status>...
 
   b) The content is non-ascii:
 
@@ -120,10 +120,10 @@
     >>> with weird_bugzilla.responses():
     ...     weird_bugzilla.initializeRemoteBugDB(remote_bugs)
     >>> for remote_bug in remote_bugs:
-    ...     print "%s: %s %s" % (
+    ...     print("%s: %s %s" % (
     ...         remote_bug,
     ...         weird_bugzilla.getRemoteStatus(remote_bug),
-    ...         weird_bugzilla.getRemoteImportance(remote_bug))
+    ...         weird_bugzilla.getRemoteImportance(remote_bug)))
     2000: ASSIGNED HIGH BLOCKER
     123543: RESOLVED FIXED HIGH BLOCKER
 

=== modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla.txt'
--- lib/lp/bugs/doc/externalbugtracker-bugzilla.txt	2018-06-23 00:51:17 +0000
+++ lib/lp/bugs/doc/externalbugtracker-bugzilla.txt	2018-06-30 11:02:51 +0000
@@ -30,29 +30,29 @@
     Traceback (most recent call last):
       ...
     UnparsableBugTrackerVersion:
-    Failed to parse version 'A.B' for http://...
+    Failed to parse version u'A.B' for http://...
 
 The version parsing is carried out by the Bugzilla._parseVersion()
 method, which takes a version string and returns a tuple of
 (major_version, minor_version).
 
     >>> external_bugzilla = Bugzilla('http://example.com')
-    >>> print external_bugzilla._parseVersion('3.2')
+    >>> print(external_bugzilla._parseVersion('3.2'))
     (3, 2)
 
 It can handle version strings with an -$foo suffix.
 
-    >>> print external_bugzilla._parseVersion('3.2-foobar')
+    >>> print(external_bugzilla._parseVersion('3.2-foobar'))
     (3, 2)
 
 And will also handle versions which contain the string 'rc'.
 
-    >>> print external_bugzilla._parseVersion('3.2rc')
+    >>> print(external_bugzilla._parseVersion('3.2rc'))
     (3, 2)
 
 + characters in the version string will be removed.
 
-    >>> print external_bugzilla._parseVersion('3.2+1')
+    >>> print(external_bugzilla._parseVersion('3.2+1'))
     (3, 2, 1)
 
 Since we don't want to depend on a working network connection, we use a
@@ -456,9 +456,9 @@
     >>> from lp.bugs.scripts.checkwatches import CheckwatchesMaster
     >>> bug_watch_updater = CheckwatchesMaster(txn, logger=FakeLogger())
     >>> for bug_watch in gnome_bugzilla.watches:
-    ...     print "%s: %s %s" % (bug_watch.remotebug,
+    ...     print("%s: %s %s" % (bug_watch.remotebug,
     ...           bug_watch.remotestatus,
-    ...           bug_watch.remote_importance)
+    ...           bug_watch.remote_importance))
     304070: None None
     3224:  None
     >>> with external_bugzilla.responses():
@@ -469,9 +469,9 @@
     http://bugzilla.gnome.org/bugs (local bugs: 15).
 
     >>> for bug_watch in gnome_bugzilla.watches:
-    ...     print "%s: %s %s" % (bug_watch.remotebug,
+    ...     print("%s: %s %s" % (bug_watch.remotebug,
     ...           bug_watch.remotestatus,
-    ...           bug_watch.remote_importance)
+    ...           bug_watch.remote_importance))
     304070: None None
     3224: RESOLVED FIXED MINOR URGENT
 
@@ -606,7 +606,7 @@
 
     >>> bug_nine = getUtility(IBugSet).get(9)
     >>> thunderbird_task = bug_nine.bugtasks[0]
-    >>> print thunderbird_task.status.title
+    >>> print(thunderbird_task.status.title)
     Unknown
     >>> thunderbird_task.bugwatch.remotestatus is None
     True
@@ -639,13 +639,13 @@
 
     >>> bug_nine = getUtility(IBugSet).get(9)
     >>> thunderbird_task = bug_nine.bugtasks[0]
-    >>> print thunderbird_task.status.title
+    >>> print(thunderbird_task.status.title)
     In Progress
-    >>> print thunderbird_task.importance.title
+    >>> print(thunderbird_task.importance.title)
     Wishlist
-    >>> print thunderbird_task.bugwatch.remotestatus
+    >>> print(thunderbird_task.bugwatch.remotestatus)
     ASSIGNED
-    >>> print thunderbird_task.bugwatch.remote_importance
+    >>> print(thunderbird_task.bugwatch.remote_importance)
     MEDIUM ENHANCEMENT
 
 If we change the bugtask status, it will be updated again even though
@@ -663,13 +663,13 @@
 
     >>> bug_nine = getUtility(IBugSet).get(9)
     >>> thunderbird_task = bug_nine.bugtasks[0]
-    >>> print thunderbird_task.status.title
+    >>> print(thunderbird_task.status.title)
     In Progress
-    >>> print thunderbird_task.importance.title
+    >>> print(thunderbird_task.importance.title)
     Wishlist
-    >>> print thunderbird_task.bugwatch.remotestatus
+    >>> print(thunderbird_task.bugwatch.remotestatus)
     ASSIGNED
-    >>> print thunderbird_task.bugwatch.remote_importance
+    >>> print(thunderbird_task.bugwatch.remote_importance)
     MEDIUM ENHANCEMENT
 
 If there are two bug watches, linked to different bugs, pointing to the
@@ -692,14 +692,14 @@
     INFO Updating 2 watches for 1 bugs on https://bugzilla.mozilla.org
 
     >>> bug_watch1 = getUtility(IBugWatchSet).get(bug_watch1_id)
-    >>> print bug_watch1.remotestatus
+    >>> print(bug_watch1.remotestatus)
     RESOLVED FIXED
-    >>> print bug_watch1.remote_importance
+    >>> print(bug_watch1.remote_importance)
     LOW BLOCKER
     >>> bug_watch2 = getUtility(IBugWatchSet).get(bug_watch2_id)
-    >>> print bug_watch2.remotestatus
+    >>> print(bug_watch2.remotestatus)
     RESOLVED FIXED
-    >>> print bug_watch2.remote_importance
+    >>> print(bug_watch2.remote_importance)
     LOW BLOCKER
 
 If updateBugWatches() can't parse the XML file returned from the remote
@@ -757,7 +757,7 @@
     >>> external_bugzilla.bug_item_file = 'gnome_bug_li_item_noproduct.xml'
     >>> with external_bugzilla.responses():
     ...     external_bugzilla.initializeRemoteBugDB(['84'])
-    >>> print external_bugzilla.getRemoteProduct('84')
+    >>> print(external_bugzilla.getRemoteProduct('84'))
     None
 
 Requesting the product for a bug that doesn't exist raises BugNotFound.

=== modified file 'lib/lp/bugs/doc/externalbugtracker-checkwatches.txt'
--- lib/lp/bugs/doc/externalbugtracker-checkwatches.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/bugs/doc/externalbugtracker-checkwatches.txt	2018-06-30 11:02:51 +0000
@@ -45,8 +45,8 @@
     ...         external_bugtracker = get_external_bugtracker(bugtracker)
     ...     finally:
     ...         log.setLevel(log.level - 100)
-    ...     print "%s --> %s" % (bugtracker_type.title,
-    ...         external_bugtracker)
+    ...     print("%s --> %s" % (bugtracker_type.title,
+    ...         external_bugtracker))
     Bugzilla --> <lp.bugs...bugzilla.Bugzilla ...>
     Debbugs --> <lp.bugs...debbugs.DebBugs ...>
     Mantis --> <lp.bugs...mantis.Mantis object at ...>

=== modified file 'lib/lp/bugs/doc/externalbugtracker-comment-imports.txt'
--- lib/lp/bugs/doc/externalbugtracker-comment-imports.txt	2015-07-21 09:04:01 +0000
+++ lib/lp/bugs/doc/externalbugtracker-comment-imports.txt	2018-06-30 11:02:51 +0000
@@ -103,10 +103,10 @@
     ...     for message in bug.messages[1:]:
     ...         bug_message = getUtility(IBugMessageSet).getByBugAndMessage(
     ...             bug, message)
-    ...         print bug_message.bugwatch == bug_watch
-    ...         print "%s: %s" % (
+    ...         print(bug_message.bugwatch == bug_watch)
+    ...         print("%s: %s" % (
     ...             bug_message.remote_comment_id,
-    ...             bug_message.message.text_contents)
+    ...             bug_message.message.text_contents))
     >>> print_bug_messages(bug, bug_watch)
     True
     1: Example comment the first
@@ -153,11 +153,11 @@
 
     >>> joe.displayname
     u'Joe Bloggs'
-    >>> print joe.preferredemail
+    >>> print(joe.preferredemail)
     None
-    >>> print joe.creation_rationale.name
+    >>> print(joe.creation_rationale.name)
     BUGIMPORT
-    >>> print joe.creation_comment
+    >>> print(joe.creation_comment)
     when importing comments for Bugzilla *TESTING* #123456.
 
 If the poster's email is already registered in Launchpad, the comment
@@ -198,7 +198,7 @@
     >>> bug.messages[-1].owner.name
     u'noemail-bugzilla-checkwatches-1'
 
-    >>> print bug.messages[-1].owner.preferredemail
+    >>> print(bug.messages[-1].owner.preferredemail)
     None
 
 A BugTrackerPerson record will have been created to map the new Person
@@ -222,7 +222,7 @@
     or display name found. (OOPS-...)
     INFO Imported 0 comments for remote bug 123456 on ...
 
-    >>> print bug.messages[-1].text_contents
+    >>> print(bug.messages[-1].text_contents)
     Yet another comment.
 
 Let's delete that comment now so that it doesn't break later tests.
@@ -302,7 +302,7 @@
     ...         content='Pushable comment', bugwatch=bug_watch2)
 
     >>> for bug_message in bug_watch2.getImportedBugMessages():
-    ...     print bug_message.message.text_contents
+    ...     print(bug_message.message.text_contents)
     Imported comment
 
     >>> transaction.commit()
@@ -359,7 +359,7 @@
     >>> bug_message_two = getUtility(IBugMessageSet).getByBugAndMessage(
     ...     bug, message_two)
 
-    >>> print bug_message_one.bugwatch
+    >>> print(bug_message_one.bugwatch)
     None
 
     >>> bug_message_two.bugwatch == bug_watch
@@ -394,7 +394,7 @@
     INFO Imported 1 comments for remote bug 123456...
 
     >>> for cve in bug_watch.bug.cves:
-    ...     print cve.displayname
+    ...     print(cve.displayname)
     CVE-1991-9911
 
 Karma is only awarded for actions that occur within Launchpad. If an
@@ -416,7 +416,7 @@
     INFO Imported 1 comments for remote bug 123456...
 
     >>> for cve in sorted([cve.displayname for cve in bug_watch.bug.cves]):
-    ...     print cve
+    ...     print(cve)
     CVE-1991-9911
     CVE-1999-0593
 
@@ -478,7 +478,7 @@
     >>> notifications[0].message.owner.name
     u'bug-watch-updater'
 
-    >>> print notifications[0].message.text_contents
+    >>> print(notifications[0].message.text_contents)
     Launchpad has imported 2 comments from the remote bug at
     http://.../show_bug.cgi?id=42.
     <BLANKLINE>
@@ -526,8 +526,8 @@
     >>> len(notifications)
     2
     >>> for notification in notifications:
-    ...     print "%s wrote: %s" % (
+    ...     print("%s wrote: %s" % (
     ...         notification.message.owner.name,
-    ...         notification.message.text_contents)
+    ...         notification.message.text_contents))
     joe-bloggs wrote: Third imported comment (initial import)
     joe-bloggs wrote: Fourth imported comment (initial import)

=== modified file 'lib/lp/bugs/doc/externalbugtracker-comment-pushing.txt'
--- lib/lp/bugs/doc/externalbugtracker-comment-pushing.txt	2015-07-08 16:05:11 +0000
+++ lib/lp/bugs/doc/externalbugtracker-comment-pushing.txt	2018-06-30 11:02:51 +0000
@@ -58,8 +58,8 @@
     ...         remote_comment_id = str(self.next_comment_id)
     ...         self.remote_comments[remote_comment_id] = comment_body
     ...
-    ...         print "Comment added as remote comment %s" % (
-    ...             remote_comment_id)
+    ...         print("Comment added as remote comment %s" % (
+    ...             remote_comment_id))
     ...
     ...         self.next_comment_id += 1
     ...         return remote_comment_id
@@ -74,7 +74,7 @@
 remote_comment_id set.  This is because it originated in Launchpad and
 has not yet been pushed to the remote bugtracker.
 
-    >>> print bug_message.remote_comment_id is None
+    >>> print(bug_message.remote_comment_id is None)
     True
 
 The IBugWatch interface defines a property, unpushed_comments, which
@@ -84,7 +84,7 @@
     >>> comments = [
     ...     comment.message.text_contents
     ...     for comment in bug_watch.unpushed_comments]
-    >>> print comments
+    >>> print(comments)
     [u'Pushing, for the purpose of.']
 
 The CheckwatchesMaster method pushBugComments() is responsible for
@@ -112,9 +112,9 @@
     ...     for message in bug.messages[1:]:
     ...         bug_message = getUtility(IBugMessageSet).getByBugAndMessage(
     ...             bug, message)
-    ...         print "%s: %s" % (
+    ...         print("%s: %s" % (
     ...             bug_message.remote_comment_id,
-    ...             bug_message.message.text_contents)
+    ...             bug_message.message.text_contents))
     >>> print_bug_messages(bug, bug_watch)
     1: Pushing, for the purpose of.
 
@@ -129,7 +129,7 @@
 If we now check the bug watch's unpushed_comments property, we will
 find it to be empty.
 
-    >>> print list(bug_watch.unpushed_comments)
+    >>> print(list(bug_watch.unpushed_comments))
     []
 
 If more comments are added to the bug they will be pushed to the remote
@@ -242,7 +242,7 @@
 
     >>> class ErroringExternalBugTracker(CommentPushingExternalBugTracker):
     ...     def addRemoteComment(self, remote_bug, comment_body, rfc822msgid):
-    ...         print "Pretending to add a comment to bug %s" % remote_bug
+    ...         print("Pretending to add a comment to bug %s" % remote_bug)
     ...         return None
 
     >>> with lp_dbuser():
@@ -273,8 +273,8 @@
 
     >>> remote_comments = external_bugtracker.remote_comments
     >>> for remote_comment_id in sorted(remote_comments.keys()):
-    ...     print remote_comments[remote_comment_id]
-    ...     print "--------------------"
+    ...     print(remote_comments[remote_comment_id])
+    ...     print("--------------------")
     Sample Person added the following comment to Launchpad bug report...:
     <BLANKLINE>
     Pushing, for the purpose of.
@@ -306,7 +306,7 @@
 the comment author, with the pushed comment.
 
     >>> formatted_message = bugwatch_updater._formatRemoteComment(message)
-    >>> print formatted_message
+    >>> print(formatted_message)
     Sample Person added the following comment to Launchpad bug report...:
     <BLANKLINE>
     Pushing, for the purpose of.
@@ -327,7 +327,7 @@
 
     >>> bugwatch_updater.external_bugtracker = external_bugtracker
     >>> formatted_message = bugwatch_updater._formatRemoteComment(message)
-    >>> print formatted_message
+    >>> print(formatted_message)
     Egg and bacon
     Egg, sausage and bacon
     Egg, bacon and bug #...

=== modified file 'lib/lp/bugs/doc/externalbugtracker-debbugs.txt'
--- lib/lp/bugs/doc/externalbugtracker-debbugs.txt	2015-09-08 09:09:28 +0000
+++ lib/lp/bugs/doc/externalbugtracker-debbugs.txt	2018-06-30 11:02:51 +0000
@@ -131,7 +131,7 @@
     >>> from lp.bugs.interfaces.bugwatch import IBugWatchSet
     >>> for bug_watch_id in bug_watch_ids:
     ...     bug_watch = getUtility(IBugWatchSet).get(bug_watch_id)
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     280883: done grave woody security
     304014: open important
     327452: done critical patch security
@@ -153,8 +153,9 @@
     >>> for bug_watch in bug_watches:
     ...     bugtasks += list(bug_watch.bugtasks)
     >>> for bugtask in sorted(bugtasks, key=operator.attrgetter('id')):
-    ...     print bugtask.bug.id, bugtask.bugtargetname, bugtask.status.title,
-    ...     print bugtask.importance.title
+    ...     print(bugtask.bug.id, bugtask.bugtargetname, bugtask.status.title,
+    ...           end=' ')
+    ...     print(bugtask.importance.title)
     1 mozilla-firefox (Debian) New Unknown
     3 mozilla-firefox (Debian Sarge) New Unknown
     7 evolution (Debian) Fix Released Unknown
@@ -185,13 +186,13 @@
 the bug has been fixed and a new package with the fix has been
 uploaded, so it maps to 'Fix Released.
 
-    >>> print external_debbugs.convertRemoteStatus('done normal').title
+    >>> print(external_debbugs.convertRemoteStatus('done normal').title)
     Fix Released
 
 If the status is simply 'open', we map it to 'New', since
 there's no way of knowing if the bug is confirmed or not.
 
-    >>> print external_debbugs.convertRemoteStatus('open normal').title
+    >>> print(external_debbugs.convertRemoteStatus('open normal').title)
     New
 
 If the 'wontfix' tag is present we map it to "Won't Fix". The 'wontfix'
@@ -203,34 +204,34 @@
 combination of 'fixed' & 'wontfix' tags will only affect the malone status
 temporarily.
 
-    >>> print external_debbugs.convertRemoteStatus(
+    >>> print(external_debbugs.convertRemoteStatus(
     ...         'open normal pending fixed fixed-in-experimental'
-    ...         ' wontfix help confirmed upstream fixed-upstream').title
+    ...         ' wontfix help confirmed upstream fixed-upstream').title)
     Won't Fix
 
 If the 'moreinfo' tag is present, we map the status to 'Needs Info'.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'open normal moreinfo').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'open normal moreinfo').title)
     Incomplete
 
 Of course, if the 'moreinfo' tag is present and the status is 'done',
 we still map to 'Fix Released'.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'done normal moreinfo').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'done normal moreinfo').title)
     Fix Released
 
 If the 'help' tag is present, it means that the maintainer is
 requesting help with the bug, so it's most likely a confirmed bug.
 
-    >>> print external_debbugs.convertRemoteStatus('open normal help').title
+    >>> print(external_debbugs.convertRemoteStatus('open normal help').title)
     Confirmed
 
 The 'pending' tag means that a fix is about to be uploaded, so it maps
 to 'Fix Committed'.
 
-    >>> print (
+    >>> print(
     ...     external_debbugs.convertRemoteStatus('open normal pending').title)
     Fix Committed
 
@@ -238,46 +239,46 @@
 somehow, but there's still an issue to be solved. We map it to 'Fix
 Committed', so that people can see that a fix is available.
 
-    >>> print external_debbugs.convertRemoteStatus('open normal fixed').title
+    >>> print(external_debbugs.convertRemoteStatus('open normal fixed').title)
     Fix Committed
 
 If the bug is forwarded upstream, it should mean that it's a confirmed
 bug.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'open normal upstream').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'open normal upstream').title)
     Confirmed
 
 And of course, if the maintainer marked the bug as 'confirmed'.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'open normal confirmed').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'open normal confirmed').title)
     Confirmed
 
 
 If it has been fixed upstream, it's definitely a confirmed bug.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'open normal fixed-upstream').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'open normal fixed-upstream').title)
     Confirmed
 
 If it has been fixed in experimental, we mark it 'Fix Committed' until
 the fix has reached the unstable distribution.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'open normal fixed-in-experimental').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'open normal fixed-in-experimental').title)
     Fix Committed
 
 All other tags we map to 'New'.
 
-    >>> print external_debbugs.convertRemoteStatus(
-    ...     'open normal unreproducible lfs woody').title
+    >>> print(external_debbugs.convertRemoteStatus(
+    ...     'open normal unreproducible lfs woody').title)
     New
 
 If we pass in a malformed status string an UnknownRemoteStatusError will
 be raised.
 
-    >>> print external_debbugs.convertRemoteStatus('open')
+    >>> print(external_debbugs.convertRemoteStatus('open'))
     Traceback (most recent call last):
       ...
     UnknownRemoteStatusError: open
@@ -299,23 +300,23 @@
     >>> report = email.message_from_file(
     ...     open(os.path.join(
     ...         test_db_location, 'db-h', '35', '322535.report')))
-    >>> print report['From']
+    >>> print(report['From'])
     Moritz Muehlenhoff <jmm@xxxxxxxxxx>
 
     >>> name, email = external_debbugs.getBugReporter('322535')
-    >>> print name
+    >>> print(name)
     Moritz Muehlenhoff
-    >>> print email
+    >>> print(email)
     jmm@xxxxxxxxxx
 
 The getBugSummaryAndDescription method reads the bug report from the
 debbugs db, and returns the debbugs subject as the summary, and the
 description as the description.
 
-    >>> print report['Subject']
+    >>> print(report['Subject'])
     evolution: Multiple format string vulnerabilities in Evolution
 
-    >>> print report.get_payload(decode=True)
+    >>> print(report.get_payload(decode=True))
     Package: evolution
     Severity: grave
     Tags: security
@@ -328,10 +329,10 @@
 
     >>> summary, description = external_debbugs.getBugSummaryAndDescription(
     ...     '322535')
-    >>> print summary
+    >>> print(summary)
     evolution: Multiple format string vulnerabilities in Evolution
 
-    >>> print description
+    >>> print(description)
     Package: evolution
     Severity: grave
     Tags: security
@@ -345,7 +346,7 @@
 Which package to file the bug against is determined by the
 getBugTargetName() method.
 
-    >>> print external_debbugs.getBugTargetName('322535')
+    >>> print(external_debbugs.getBugTargetName('322535'))
     evolution
 
 
@@ -390,7 +391,7 @@
 bug. DebBugs comment IDs are RFC822 message IDs.
 
     >>> comment_ids = external_debbugs.getCommentIds(bug_watch.remotebug)
-    >>> print comment_ids
+    >>> print(comment_ids)
     ['<20040309081430.98BF411EE67@tux>']
 
 However, it will only return IDs for comments which can actually be
@@ -400,7 +401,7 @@
     ...     'debbugs-comment-with-no-date.txt')
 
     >>> comment_ids = external_debbugs.getCommentIds(bug_watch.remotebug)
-    >>> print comment_ids
+    >>> print(comment_ids)
     []
 
 getCommentIds() will only return a given comment ID once, even if that
@@ -418,14 +419,14 @@
     >>> debian_bug = external_debbugs._findBug(bug_watch.remotebug)
     >>> for comment in debian_bug.comments:
     ...     comment_email = email.message_from_string(comment)
-    ...     print comment_email['message-id']
+    ...     print(comment_email['message-id'])
     <20040309081430.98BF411EE67@tux>
     <20040309081430.98BF411EE67@tux>
 
 However, getCommentIds() will only return the comment ID once.
 
     >>> comment_ids = external_debbugs.getCommentIds(bug_watch.remotebug)
-    >>> print comment_ids
+    >>> print(comment_ids)
     ['<20040309081430.98BF411EE67@tux>']
 
 The debbugs implementation of fetchComments() doesn't actually do
@@ -441,7 +442,7 @@
     >>> comment_id = comment_ids[0]
     >>> poster_name, poster_email = external_debbugs.getPosterForComment(
     ...     bug_watch.remotebug, comment_id)
-    >>> print "%s <%s>" % (poster_name, poster_email)
+    >>> print("%s <%s>" % (poster_name, poster_email))
     Teun Vink <teun@xxxxxxxxxxxxxxxxxxx>
 
 getMessageForComment() will return an imported comment as a Launchpad
@@ -459,10 +460,10 @@
     >>> message = external_debbugs.getMessageForComment(
     ...     bug_watch.remotebug, comment_id, poster)
 
-    >>> print message.owner.displayname
+    >>> print(message.owner.displayname)
     Teun Vink
 
-    >>> print message.text_contents
+    >>> print(message.text_contents)
     Things happen.
 
 Where the DebBugs comment specifies a date in its Received header,
@@ -476,14 +477,14 @@
     ...     'debbugs-comment-with-received-date.txt')
 
     >>> comment_ids = external_debbugs.getCommentIds(bug_watch.remotebug)
-    >>> print comment_ids
+    >>> print(comment_ids)
     ['<yetanothermessageid@launchpad>']
 
     >>> external_debbugs.fetchComments(bug_watch, comment_ids)
     >>> message = external_debbugs.getMessageForComment(
     ...     bug_watch.remotebug, comment_ids[0], poster)
 
-    >>> print message.datecreated
+    >>> print(message.datecreated)
     2008-05-30 21:18:12+00:00
 
 If we parse the comment manually we'll see that the message's
@@ -494,10 +495,10 @@
     >>> parsed_message = email.message_from_string(
     ...     read_test_file('debbugs-comment-with-received-date.txt'))
 
-    >>> print parsed_message['date']
+    >>> print(parsed_message['date'])
     Fri, 14 Dec 2007 18:54:30 +0000
 
-    >>> print parsed_message['received']
+    >>> print(parsed_message['received'])
     (at 220301) by example.com; 30 May 2008 21:18:12 +0000
 
 However, if none of the Received headers don't match the hostname that
@@ -513,16 +514,16 @@
     >>> message = external_debbugs.getMessageForComment(
     ...     bug_watch.remotebug, comment_ids[0], poster)
 
-    >>> print message.datecreated
+    >>> print(message.datecreated)
     2007-12-14 18:54:30+00:00
 
     >>> parsed_message = email.message_from_string(
     ...     read_test_file('debbugs-comment-with-received-date.txt'))
 
-    >>> print parsed_message['date']
+    >>> print(parsed_message['date'])
     Fri, 14 Dec 2007 18:54:30 +0000
 
-    >>> print parsed_message['received']
+    >>> print(parsed_message['received'])
     (at 220301) by example.com; 30 May 2008 21:18:12 +0000
 
 DebBugs has a method, _getDateForComment(), which returns the correct
@@ -535,7 +536,7 @@
 If the message has no Date or useful Received headers,
 _getDateForComment() will return None.
 
-    >>> print external_debbugs._getDateForComment(test_message)
+    >>> print(external_debbugs._getDateForComment(test_message))
     None
 
 If the message has only a Date header, that will be returned as the
@@ -591,19 +592,19 @@
 
     >>> transaction.commit()
 
-    >>> external_debbugs.addRemoteComment(
+    >>> print(external_debbugs.addRemoteComment(
     ...     '1234', 'A little fermented curd will do the trick!',
-    ...     '<123456@xxxxxxxxxxxxx>')
-    '<123456@xxxxxxxxxxxxx>'
+    ...     '<123456@xxxxxxxxxxxxx>'))
+    <123456@xxxxxxxxxxxxx>
 
 We can look for the mail that would have been sent.
 
     >>> from lp.testing.mail_helpers import pop_notifications
     >>> [msg] = pop_notifications()
-    >>> print msg['X-Envelope-To']
+    >>> print(msg['X-Envelope-To'])
     1234@xxxxxxxxxxx
 
-    >>> print str(msg)
+    >>> print(str(msg))
     From ...
     Content-Type...
     Message-Id: <123456@xxxxxxxxxxxxx>
@@ -648,7 +649,7 @@
     >>> (out, err) = process.communicate()
     >>> process.returncode
     0
-    >>> print err
+    >>> print(err)
     INFO    Updating 1 watches for 1 bugs on http://bugs.debian.org
     INFO    Imported 4 comments for remote bug 237001...
     INFO    Imported debbugs #237001 as Launchpad bug #...
@@ -670,7 +671,7 @@
 
     >>> [imported_bug] = debbugs.getBugsWatching('237001')
     >>> for bugtask in imported_bug.bugtasks:
-    ...     print "%s: %s" % (bugtask.bugtargetname, bugtask.status.name)
+    ...     print("%s: %s" % (bugtask.bugtargetname, bugtask.status.name))
     evolution (Ubuntu): NEW
     evolution (Debian): NEW
 
@@ -704,13 +705,13 @@
     >>> external_debbugs = DebBugs(
     ...     'http://example.com/', db_location=test_db_location)
 
-    >>> print external_debbugs.getRemoteProduct('237001')
+    >>> print(external_debbugs.getRemoteProduct('237001'))
     evolution
 
 Trying to call getRemoteProduct() on a bug that doesn't exist will raise
 a BugNotFound error.
 
-    >>> print external_debbugs.getRemoteProduct('42')
+    >>> print(external_debbugs.getRemoteProduct('42'))
     Traceback (most recent call last):
       ...
     BugNotFound: 42

=== modified file 'lib/lp/bugs/doc/externalbugtracker-emailaddress.txt'
--- lib/lp/bugs/doc/externalbugtracker-emailaddress.txt	2015-03-13 19:05:50 +0000
+++ lib/lp/bugs/doc/externalbugtracker-emailaddress.txt	2018-06-30 11:02:51 +0000
@@ -114,9 +114,9 @@
 Similarly, Bug.getBugWatch() will always return None for email address
 bug trackers.
 
-    >>> print example_bug.getBugWatch(email_tracker, '')
+    >>> print(example_bug.getBugWatch(email_tracker, ''))
     None
 
-    >>> print example_bug.getBugWatch(email_tracker, message_id)
+    >>> print(example_bug.getBugWatch(email_tracker, message_id))
     None
 

=== modified file 'lib/lp/bugs/doc/externalbugtracker-linking-back.txt'
--- lib/lp/bugs/doc/externalbugtracker-linking-back.txt	2015-07-08 16:05:11 +0000
+++ lib/lp/bugs/doc/externalbugtracker-linking-back.txt	2018-06-30 11:02:51 +0000
@@ -19,13 +19,13 @@
     ...         self.last_launchpad_bug_id = None
     ...
     ...     def getLaunchpadBugId(self, remote_bug):
-    ...         print "Getting Launchpad id for bug %s" % remote_bug
+    ...         print("Getting Launchpad id for bug %s" % remote_bug)
     ...         return self.last_launchpad_bug_id
     ...
     ...     def setLaunchpadBugId(self, remote_bug, launchpad_bug_id,
     ...                           launchpad_bug_url):
     ...         self.last_launchpad_bug_id = launchpad_bug_id
-    ...         print "Setting Launchpad id for bug %s" % remote_bug
+    ...         print("Setting Launchpad id for bug %s" % remote_bug)
 
 The methods are called by the CheckwatchesMaster class:
 
@@ -111,8 +111,8 @@
 
 unlinked_bug doesn't link to bug 42 on the remote bug tracker.
 
-    >>> print unlinked_bug.getBugWatch(
-    ...     bug_watch.bugtracker, bug_watch.remotebug)
+    >>> print(unlinked_bug.getBugWatch(
+    ...     bug_watch.bugtracker, bug_watch.remotebug))
     None
 
 However, the remote bug currently thinks that unlinked_bug does in

=== modified file 'lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt'
--- lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt	2018-06-23 00:23:41 +0000
+++ lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt	2018-06-30 11:02:51 +0000
@@ -73,7 +73,7 @@
 Collect the Example.com watches:
 
     >>> for bug_watch in example_bug_tracker.watches:
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     3224: None
 
 And have our special Mantis instance process them:
@@ -90,7 +90,7 @@
     INFO Updating 1 watches for 1 bugs on http://bugs.some.where
 
     >>> for bug_watch in example_bug_tracker.watches:
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     3224: assigned: open
 
 Let's add a few more watches:
@@ -143,9 +143,9 @@
     >>> for remote_bug_id in sorted(remote_bug_ids):
     ...     remote_status = remote_statuses[remote_bug_id]
     ...     expected_remote_status = expected_remote_statuses[remote_bug_id]
-    ...     print 'Remote bug %d' % (remote_bug_id,)
-    ...     print ' * Expected << %s >>' % (expected_remote_status,)
-    ...     print ' *      Got << %s >>' % (remote_status,)
+    ...     print('Remote bug %d' % (remote_bug_id,))
+    ...     print(' * Expected << %s >>' % (expected_remote_status,))
+    ...     print(' *      Got << %s >>' % (remote_status,))
     Remote bug 3224
      * Expected << assigned: open >>
      *      Got << assigned: open >>

=== modified file 'lib/lp/bugs/doc/externalbugtracker-mantis.txt'
--- lib/lp/bugs/doc/externalbugtracker-mantis.txt	2018-06-23 00:23:41 +0000
+++ lib/lp/bugs/doc/externalbugtracker-mantis.txt	2018-06-30 11:02:51 +0000
@@ -73,7 +73,7 @@
 Collect the Example.com watches:
 
     >>> for bug_watch in example_bug_tracker.watches:
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     1550: None
 
 And have our special Mantis instance process them:
@@ -92,7 +92,7 @@
     INFO Updating 1 watches for 1 bugs on http://bugs.some.where
 
     >>> for bug_watch in example_bug_tracker.watches:
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     1550: assigned: open
 
 Let's add a few more watches:
@@ -155,9 +155,9 @@
     ...     expected_remote_statuses)):
     ...     remote_status = remote_statuses[remote_bug_id]
     ...     expected_remote_status = expected_remote_statuses[remote_bug_id]
-    ...     print 'Remote bug %d' % (remote_bug_id,)
-    ...     print ' * Expected << %s >>' % (expected_remote_status,)
-    ...     print ' *      Got << %s >>' % (remote_status,)
+    ...     print('Remote bug %d' % (remote_bug_id,))
+    ...     print(' * Expected << %s >>' % (expected_remote_status,))
+    ...     print(' *      Got << %s >>' % (remote_status,))
     Remote bug 1550
      * Expected << assigned: open >>
      *      Got << assigned: open >>

=== modified file 'lib/lp/bugs/doc/externalbugtracker-roundup-python-bugs.txt'
--- lib/lp/bugs/doc/externalbugtracker-roundup-python-bugs.txt	2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/doc/externalbugtracker-roundup-python-bugs.txt	2018-06-30 11:02:51 +0000
@@ -50,8 +50,8 @@
     >>> for status, resolution in python_bugs_example_statuses:
     ...     status_string = convert_python_status(status, resolution)
     ...     status_converted = python_bugs.convertRemoteStatus(status_string)
-    ...     print '(%s, %s) --> %s --> %s' % (
-    ...         status, resolution, status_string, status_converted)
+    ...     print('(%s, %s) --> %s --> %s' % (
+    ...         status, resolution, status_string, status_converted))
     (open, None) --> 1:None --> New
     (open, accepted) --> 1:1 --> Confirmed
     (open, duplicate) --> 1:2 --> Confirmed

=== modified file 'lib/lp/bugs/doc/externalbugtracker-rt.txt'
--- lib/lp/bugs/doc/externalbugtracker-rt.txt	2018-06-22 22:07:42 +0000
+++ lib/lp/bugs/doc/externalbugtracker-rt.txt	2018-06-30 11:02:51 +0000
@@ -257,13 +257,13 @@
 concept of products, only queues, so though there'e a terminology
 mismatch the meaning is essentially the same.
 
-    >>> print rt.getRemoteProduct(1585)
+    >>> print(rt.getRemoteProduct(1585))
     OpenSSL-Bugs
 
 If you try to get the remote product of a bug that doesn't exist you'll
 get a BugNotFound error.
 
-    >>> print rt.getRemoteProduct('this-doesnt-exist')
+    >>> print(rt.getRemoteProduct('this-doesnt-exist'))
     Traceback (most recent call last):
       ...
     BugNotFound: this-doesnt-exist
@@ -272,5 +272,5 @@
 bug, getRemoteProduct() will return None.
 
     >>> del rt.bugs[1589]['queue']
-    >>> print rt.getRemoteProduct(1589)
+    >>> print(rt.getRemoteProduct(1589))
     None

=== modified file 'lib/lp/bugs/doc/externalbugtracker-sourceforge.txt'
--- lib/lp/bugs/doc/externalbugtracker-sourceforge.txt	2018-06-23 00:12:24 +0000
+++ lib/lp/bugs/doc/externalbugtracker-sourceforge.txt	2018-06-30 11:02:51 +0000
@@ -120,7 +120,7 @@
     >>> page_data = open(
     ...     'lib/lp/bugs/tests/testfiles/'
     ...     'sourceforge-sample-bug-99.html')
-    >>> print sourceforge._extractErrorMessage(page_data)
+    >>> print(sourceforge._extractErrorMessage(page_data))
     Artifact: This Artifact Has Been Made Private. Only Group Members
     Can View Private ArtifactTypes.
 
@@ -266,13 +266,13 @@
 belongs. The remote product is returned by getRemoteProduct() as an
 ampersand-separated string.
 
-    >>> print sourceforge.getRemoteProduct('1722250')
+    >>> print(sourceforge.getRemoteProduct('1722250'))
     155120&794532
 
 If you try to get the remote product of a bug that doesn't exist you'll
 get a BugNotFound error.
 
-    >>> print sourceforge.getRemoteProduct(999999999)
+    >>> print(sourceforge.getRemoteProduct(999999999))
     Traceback (most recent call last):
       ...
     BugNotFound: 999999999
@@ -284,5 +284,5 @@
 
     >>> with sourceforge.responses():
     ...     sourceforge.initializeRemoteBugDB([99])
-    >>> print sourceforge.getRemoteProduct(99)
+    >>> print(sourceforge.getRemoteProduct(99))
     None

=== modified file 'lib/lp/bugs/doc/externalbugtracker-trac-lp-plugin.txt'
--- lib/lp/bugs/doc/externalbugtracker-trac-lp-plugin.txt	2018-06-23 02:00:08 +0000
+++ lib/lp/bugs/doc/externalbugtracker-trac-lp-plugin.txt	2018-06-30 11:02:51 +0000
@@ -54,7 +54,7 @@
     ...                 raise AssertionError(
     ...                     "Token has already been consumed.")
     ...             token.consume()
-    ...             print "Successfully validated the token."
+    ...             print("Successfully validated the token.")
     ...             cookie_string = (
     ...                 'trac_auth=random_token-' + str(random.random()))
     ...             self._xmlrpc_transport.setCookie(cookie_string)
@@ -94,7 +94,7 @@
 auth_cookie attribute set.
 
     >>> test_transport.cookie_jar
-    <RequestsCookieJar[Cookie(version=0, name='trac_auth'...
+    <RequestsCookieJar[Cookie(version=0, name=u'trac_auth'...
 
 The XML-RPC transport shares its cookiejar with the TracLPPlugin instance.
 This is so that the TracLPPlugin can use the cookiejar when authenticating
@@ -107,7 +107,7 @@
 So if we look in the TracLPPlugin's CookieJar we'll see the same cookie:
 
     >>> trac._cookie_jar
-    <RequestsCookieJar[Cookie(version=0, name='trac_auth'...
+    <RequestsCookieJar[Cookie(version=0, name=u'trac_auth'...
 
 And altering the cookie in the TracLPPlugin's CookieJar will mean, of
 course, that it's altered in the XML-RPC transport's CookieJar, too.
@@ -118,12 +118,12 @@
     ...     domain='http://example.com', path='')
 
     >>> trac._cookie_jar
-    <...CookieJar[Cookie(version=0, name='trac_auth',
-    value='Look ma, a new cookie!'...>
+    <...CookieJar[Cookie(version=0, name=u'trac_auth',
+    value=u'Look ma, a new cookie!'...>
 
     >>> test_transport.cookie_jar
-    <...CookieJar[Cookie(version=0, name='trac_auth',
-    value='Look ma, a new cookie!'...>
+    <...CookieJar[Cookie(version=0, name=u'trac_auth',
+    value=u'Look ma, a new cookie!'...>
 
 If authentication fails, a BugTrackerAuthenticationError will be raised.
 
@@ -170,7 +170,7 @@
 an authorization request.
 
     >>> test_transport.auth_cookie
-    Cookie(version=0, name='trac_auth'...)
+    Cookie(version=0, name=u'trac_auth'...)
     >>> trac.getCurrentDBTime()
     datetime.datetime(2008, 4, 9, 2, 2, 1, tzinfo=<UTC>)
 
@@ -213,10 +213,13 @@
     >>> last_checked = datetime(2008, 1, 1, 0, 0, 0)
     >>> test_transport.expireCookie(test_transport.auth_cookie)
     >>> with trac.responses():
-    ...     sorted(trac.getModifiedRemoteBugs(bug_ids_to_check, last_checked))
+    ...     for bug_id in sorted(trac.getModifiedRemoteBugs(
+    ...             bug_ids_to_check, last_checked)):
+    ...         print(bug_id)
     Using XML-RPC to generate token.
     Successfully validated the token.
-    ['1', '3']
+    1
+    3
 
 Different last_checked times will result in different numbers of bugs
 being returned.
@@ -224,10 +227,12 @@
     >>> last_checked = datetime(2008, 2, 1, 0, 0, 0)
     >>> test_transport.expireCookie(test_transport.auth_cookie)
     >>> with trac.responses():
-    ...     trac.getModifiedRemoteBugs(bug_ids_to_check, last_checked)
+    ...     for bug_id in sorted(trac.getModifiedRemoteBugs(
+    ...             bug_ids_to_check, last_checked)):
+    ...         print(bug_id)
     Using XML-RPC to generate token.
     Successfully validated the token.
-    ['1']
+    1
 
 If no bugs have been updated since last_checked, getModifiedRemoteBugs()
 will return an empty list.
@@ -248,10 +253,15 @@
     >>> last_checked = datetime(2008, 1, 1, 0, 0, 0)
     >>> test_transport.expireCookie(test_transport.auth_cookie)
     >>> with trac.responses():
-    ...     sorted(trac.getModifiedRemoteBugs(bug_ids_to_check, last_checked))
+    ...     for bug_id in sorted(trac.getModifiedRemoteBugs(
+    ...             bug_ids_to_check, last_checked)):
+    ...         print(bug_id)
     Using XML-RPC to generate token.
     Successfully validated the token.
-    ['1', '100', '3', '99']
+    1
+    100
+    3
+    99
 
 
 Getting the status of remote bugs
@@ -281,11 +291,11 @@
 Calling getRemoteStatus() on our example TracLPPlugin instance will
 return the status for whichever bug we request.
 
-    >>> trac.getRemoteStatus('1')
-    'open'
+    >>> print(trac.getRemoteStatus('1'))
+    open
 
-    >>> trac.getRemoteStatus('3')
-    'reopened'
+    >>> print(trac.getRemoteStatus('3'))
+    reopened
 
 If we try to get the status of bug 2 we'll get a BugNotFound error,
 since that bug wasn't in the list of bugs that were modified since our
@@ -366,13 +376,16 @@
     Using XML-RPC to generate token.
     Successfully validated the token.
 
-    >>> trac.getCommentIds(bug_watch.remotebug)
-    ['1-1']
+    >>> for comment_id in trac.getCommentIds(bug_watch.remotebug):
+    ...     print(comment_id)
+    1-1
 
 bug_watch_two is against remote bug 2, which has two comments.
 
-    >>> trac.getCommentIds(bug_watch_two.remotebug)
-    ['2-1', '2-2']
+    >>> for comment_id in trac.getCommentIds(bug_watch_two.remotebug):
+    ...     print(comment_id)
+    2-1
+    2-2
 
 bug_watch_three is against bug 3, which has no comments.
 
@@ -393,8 +406,9 @@
 Before fetchComments() is called for a given remote bug, that remote
 bug's 'comments' field will be a list of comment IDs.
 
-    >>> trac.bugs[1]['comments']
-    ['1-1']
+    >>> for comment_id in trac.bugs[1]['comments']:
+    ...     print(comment_id)
+    1-1
 
 After fetchComments() is called the bug's 'comments' field will contain
 a dict in the form {<comment_id>: <comment_dict>}, which can then be
@@ -411,7 +425,7 @@
 
     >>> for comment in trac.bugs[1]['comments'].values():
     ...     for key in sorted(comment.keys()):
-    ...         print "%s: %s" % (key, comment[key])
+    ...         print("%s: %s" % (key, comment[key]))
     comment: Hello, world!
     id: 1-1
     timestamp: 1208518200
@@ -421,8 +435,10 @@
 getPosterForComment() returns a tuple of (displayname, emailaddress) for
 the poster of a given comment.
 
-    >>> trac.getPosterForComment(bug_watch.remotebug, '1-1')
-    ('Test', 'test@xxxxxxxxxxxxx')
+    >>> display_name, email = trac.getPosterForComment(
+    ...     bug_watch.remotebug, '1-1')
+    >>> print(display_name, email)
+    Test test@xxxxxxxxxxxxx
 
 getPosterForComment() handles situations in which only an email address
 is supplied for the 'user' field by returning None as the user's
@@ -433,16 +449,19 @@
     >>> transaction.commit()
 
     >>> trac.fetchComments(remote_bug, ['2-1', '2-2'])
-    >>> trac.getPosterForComment(remote_bug, '2-1')
-    (None, 'test@xxxxxxxxxxxxx')
+    >>> display_name, email = trac.getPosterForComment(remote_bug, '2-1')
+    >>> print(display_name, email)
+    None test@xxxxxxxxxxxxx
 
 getPosterForComment() will also return displayname, email tuples in
 cases where the 'user' field is set to a plain username (e.g. 'foo').
 However, in these cases it is the email address that will be set to
 None.
 
-    >>> trac.getPosterForComment(bug_watch_two.remotebug, '2-2')
-    ('foo.bar', None)
+    >>> display_name, email = trac.getPosterForComment(
+    ...     bug_watch_two.remotebug, '2-2')
+    >>> print(display_name, email)
+    foo.bar None
 
 Finally, getMessageForComment() will return a Message instance for a
 given comment. For the sake of brevity we'll use test@xxxxxxxxxxxxx as
@@ -456,13 +475,13 @@
 The Message returned by getMessageForComment() contains the full text of
 the original comment.
 
-    >>> print message_one.text_contents
+    >>> print(message_one.text_contents)
     Hello, world!
 
 The owner of the comment is set to the Person passed to
 getMessageForComment().
 
-    >>> print message_one.owner.displayname
+    >>> print(message_one.owner.displayname)
     Sample Person
 
 
@@ -513,7 +532,7 @@
     Using XML-RPC to generate token.
     Successfully validated the token.
 
-    >>> print remote_comment_id
+    >>> print(remote_comment_id)
     3-1
 
 If we look at our example remote server we can see that the comment has
@@ -521,7 +540,7 @@
 
     >>> for comment in test_transport.remote_bugs['3'].comments:
     ...     for key in sorted(comment.keys()):
-    ...         print "%s: %s" % (key, comment[key])
+    ...         print("%s: %s" % (key, comment[key]))
     comment: An example comment to push.
     id: 3-1
     time: ...
@@ -554,7 +573,7 @@
     Using XML-RPC to generate token.
     Successfully validated the token.
 
-    >>> print launchpad_bug_id
+    >>> print(launchpad_bug_id)
     None
 
 We call setLaunchpadBugId() to set the Launchpad bug ID for a remote
@@ -570,14 +589,14 @@
 Calling getLaunchpadBugId() for remote bug 3 will now return 10, since
 that's the Launchpad bug ID that we've just set.
 
-    >>> print trac.getLaunchpadBugId('3')
+    >>> print(trac.getLaunchpadBugId('3'))
     15
 
 Passing a Launchpad bug ID of None to setLaunchpadBugId() will unset the
 Launchpad bug ID for the remote bug.
 
     >>> trac.setLaunchpadBugId('3', None, None)
-    >>> print trac.getLaunchpadBugId('3')
+    >>> print(trac.getLaunchpadBugId('3'))
     None
 
 If we try to call getLaunchpadBugId() or setLaunchpadBugId() for a

=== modified file 'lib/lp/bugs/doc/externalbugtracker-trac.txt'
--- lib/lp/bugs/doc/externalbugtracker-trac.txt	2018-06-23 02:00:08 +0000
+++ lib/lp/bugs/doc/externalbugtracker-trac.txt	2018-06-30 11:02:51 +0000
@@ -41,8 +41,8 @@
     ...     chosen_trac = trac.getExternalBugTrackerToUse()
     >>> isinstance(chosen_trac, TracLPPlugin)
     True
-    >>> chosen_trac.baseurl
-    'http://example.com'
+    >>> print(chosen_trac.baseurl)
+    http://example.com
 
 Some Trac instances in the wild return HTTP 200 when the resource is
 not found (HTTP 404 would be more appropriate). A distinguishing
@@ -60,8 +60,8 @@
     ...     chosen_trac = trac.getExternalBugTrackerToUse()
     >>> isinstance(chosen_trac, TracLPPlugin)
     False
-    >>> chosen_trac.baseurl
-    'http://example.com'
+    >>> print(chosen_trac.baseurl)
+    http://example.com
 
 In the event that our deliberately bogus token is considered valid,
 the external bug tracker that groks the plugin is selected:
@@ -73,8 +73,8 @@
     ...     chosen_trac = trac.getExternalBugTrackerToUse()
     >>> isinstance(chosen_trac, TracLPPlugin)
     True
-    >>> chosen_trac.baseurl
-    'http://example.com'
+    >>> print(chosen_trac.baseurl)
+    http://example.com
 
 If a 404 is returned, the normal Trac instance is returned.
 
@@ -249,7 +249,7 @@
 the IDs returned are nothing like the ones we asked for:
 
     >>> bug_ids = sorted(int(bug['id']) for bug in remote_bugs)
-    >>> print bug_ids
+    >>> print(bug_ids)
     [1, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153]
 
 If _fetchBugData() receives a response that isn't a valid Trac CSV
@@ -286,7 +286,7 @@
 Collect the Example.com watches:
 
     >>> for bug_watch in example_bug_tracker.watches:
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     1: None
 
 And have a Trac instance process them:
@@ -306,7 +306,7 @@
     INFO Updating 1 watches for 1 bugs on http://bugs.some.where
 
     >>> for bug_watch in example_bug_tracker.watches:
-    ...     print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus))
     1: fixed
 
 We'll add some more watches now.
@@ -320,7 +320,7 @@
 
     >>> for remote_bug, bug_watch_id in bug_watches.items():
     ...     bug_watch = getUtility(IBugWatchSet).get(bug_watch_id)
-    ...     print "%s: %s" % (remote_bug, bug_watch.remotestatus)
+    ...     print("%s: %s" % (remote_bug, bug_watch.remotestatus))
     1: fixed
 
     >>> remote_bugs = [
@@ -354,7 +354,7 @@
     ...     bug_watch = getUtility(IBugWatchSet).get(
     ...         bug_watches[remote_bug_id])
     ...     remote_status = bug_watch.remotestatus
-    ...     print 'Remote bug %d: %s' % (remote_bug_id, remote_status)
+    ...     print('Remote bug %d: %s' % (remote_bug_id, remote_status))
     Remote bug 1: fixed
     Remote bug 143: fixed
     Remote bug 144: assigned

=== modified file 'lib/lp/bugs/doc/externalbugtracker.txt'
--- lib/lp/bugs/doc/externalbugtracker.txt	2016-07-04 17:08:29 +0000
+++ lib/lp/bugs/doc/externalbugtracker.txt	2018-06-30 11:02:51 +0000
@@ -26,8 +26,8 @@
     ...     TestExternalBugTracker)
     >>> class InitializingExternalBugTracker(TestExternalBugTracker):
     ...     def initializeRemoteBugDB(self, remote_bug_ids):
-    ...         print "initializeRemoteBugDB() called: %r" % (
-    ...             remote_bug_ids, )
+    ...         print("initializeRemoteBugDB() called: %r" % (
+    ...             remote_bug_ids, ))
 
     >>> from lp.services.log.logger import FakeLogger
     >>> from lp.bugs.scripts.checkwatches import CheckwatchesMaster
@@ -76,7 +76,7 @@
     ...     bug_tracker_to_use = Bugzilla
     ...
     ...     def getExternalBugTrackerToUse(self):
-    ...         print "Getting external bugtracker to use"
+    ...         print("Getting external bugtracker to use")
     ...         return self.bug_tracker_to_use(self.baseurl)
 
 We'll create a helper method to allow us to avoid having to connect to a
@@ -180,7 +180,7 @@
     ...         }
     ...
     ...     def getProductsForRemoteBugs(self, bug_ids):
-    ...         print "Getting products for remote bugs"
+    ...         print("Getting products for remote bugs")
     ...         mappings = {}
     ...         for bug_id in bug_ids:
     ...             if int(bug_id) in self.remote_bug_products:
@@ -241,7 +241,7 @@
 
     >>> from operator import attrgetter
     >>> for watch in sorted(sync_watches, key=attrgetter('remotebug')):
-    ...     print watch.remotebug
+    ...     print(watch.remotebug)
     1
     3
 
@@ -252,7 +252,7 @@
     False
 
     >>> for watch in sorted(other_watches, key=attrgetter('remotebug')):
-    ...     print watch.remotebug
+    ...     print(watch.remotebug)
     2
     4
     5
@@ -289,14 +289,14 @@
     False
 
     >>> for watch in sorted(sync_watches, key=attrgetter('remotebug')):
-    ...     print watch.remotebug
+    ...     print(watch.remotebug)
     1
     2
     3
     5
 
     >>> for watch in sorted(other_watches, key=attrgetter('remotebug')):
-    ...     print watch.remotebug
+    ...     print(watch.remotebug)
     4
 
 If there are no syncable GNOME products, only one batch is returned,
@@ -324,7 +324,7 @@
     >>> class TimeUnknownExternalBugTracker(InitializingExternalBugTracker):
     ...
     ...     def getCurrentDBTime(self):
-    ...         print "getCurrentDBTime() called"
+    ...         print("getCurrentDBTime() called")
     ...         return None
 
     >>> bug_watch_updater.updateBugWatches(
@@ -434,12 +434,12 @@
     ...     def getCurrentDBTime(self):
     ...         return datetime.now(pytz.timezone('UTC'))
     ...     def getModifiedRemoteBugs(self, remote_bug_ids, last_checked):
-    ...         print "last_checked: %s" % last_checked
-    ...         print "getModifiedRemoteBugs() called: %r" % (
-    ...             remote_bug_ids, )
+    ...         print("last_checked: %s" % last_checked)
+    ...         print("getModifiedRemoteBugs() called: %r" % (
+    ...             remote_bug_ids, ))
     ...         return [remote_bug_ids[0], remote_bug_ids[-1]]
     ...     def getRemoteStatus(self, bug_id):
-    ...         print "getRemoteStatus() called: %r" % bug_id
+    ...         print("getRemoteStatus() called: %r" % bug_id)
     ...         return 'UNKNOWN'
 
 Only bugs that have been checked before are passed on to
@@ -498,7 +498,7 @@
     ...     external_bugtracker, bug_watches,
     ...     external_bugtracker.getCurrentDBTime())
     >>> for key in sorted(ids):
-    ...     print "%s: %s" % (key, sorted(ids[key]))
+    ...     print("%s: %s" % (key, sorted(ids[key])))
     all_remote_ids: [u'1', u'2', u'3', u'4']
     remote_ids_to_check: [u'1', u'2', u'3', u'4']
     unmodified_remote_ids: []
@@ -530,7 +530,7 @@
     last_checked: 2007-03-17 15:...:...
 
     >>> for key in sorted(ids):
-    ...     print "%s: %s" % (key, sorted(ids[key]))
+    ...     print("%s: %s" % (key, sorted(ids[key])))
     all_remote_ids: [u'1', u'2', u'3', u'4']
     remote_ids_to_check: [u'1', u'4']
     unmodified_remote_ids: [u'2', u'3']
@@ -548,9 +548,9 @@
 
     >>> for bug_watch in bug_watches:
     ...     if bug_watch.lastchecked > some_time_ago:
-    ...         print "Bug %s was marked checked" % bug_watch.remotebug
+    ...         print("Bug %s was marked checked" % bug_watch.remotebug)
     ...     else:
-    ...         print "Bug %s was NOT marked checked" % bug_watch.remotebug
+    ...         print("Bug %s was NOT marked checked" % bug_watch.remotebug)
     Bug 1 was marked checked
     Bug 2 was marked checked
     Bug 3 was marked checked
@@ -625,10 +625,10 @@
     >>> ids = bug_watch_updater._getRemoteIdsToCheck(
     ...     external_bugtracker, bug_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     []
 
-    >>> print sorted(ids['unmodified_remote_ids'])
+    >>> print(sorted(ids['unmodified_remote_ids']))
     [u'1', u'2', u'3', u'4']
 
     >>> comment_message = factory.makeMessage(
@@ -641,7 +641,7 @@
     >>> ids = bug_watch_updater._getRemoteIdsToCheck(
     ...     external_bugtracker, bug_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'4']
 
 Once the comment has been pushed it will no longer appear in the list of
@@ -652,7 +652,7 @@
     >>> ids = bug_watch_updater._getRemoteIdsToCheck(
     ...     external_bugtracker, bug_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     []
 
 
@@ -734,8 +734,8 @@
     ...             external_bugtracker, [example_bugwatch])
     ...     except error:
     ...         pass
-    ...     print "%s: %s" % (example_bugwatch.last_error_type.title,
-    ...         example_bugwatch.lastchecked is not None)
+    ...     print("%s: %s" % (example_bugwatch.last_error_type.title,
+    ...         example_bugwatch.lastchecked is not None))
     Connection Error: True
     Unparsable Bug: True
     Unparsable Bug Tracker Version: True
@@ -761,10 +761,10 @@
     ...     bug_watch_updater.updateBugWatches(
     ...         external_bugtracker, [example_bugwatch])
     ...     oops = capture.oopses[-1]
-    ...     print "%s: %s (%s; %s)" % (
+    ...     print("%s: %s (%s; %s)" % (
     ...         example_bugwatch.last_error_type.title,
     ...         example_bugwatch.lastchecked is not None,
-    ...         oops['id'], oops['url'])
+    ...         oops['id'], oops['url']))
     Unparsable Bug: True (OOPS-...; http://bugs.example.com/show_bug.cgi?id=1)
     Unknown: True (OOPS-...; http://bugs.example.com/show_bug.cgi?id=1)
 
@@ -806,7 +806,7 @@
 is always assumed to be a member of `BugTaskStatus`.
 
     >>> for line in tree.moinmoin_table():
-    ...     print line
+    ...     print(line)
     || ASSIGNED '''or''' STARTED || - (''ignored'') || In Progress ||
     || NEEDINFO '''or''' WAITING '''or''' SUSPENDED || - (''ignored'') ...
     || PENDINGUPLOAD '''or''' RELEASE_PENDING || - (''ignored'') || Fix...
@@ -822,7 +822,7 @@
 
     >>> titles = ('Status', 'Resolution', 'LP status')
     >>> for line in tree.moinmoin_table(titles):
-    ...     print line
+    ...     print(line)
     || '''Status''' || '''Resolution''' || '''LP status''' ||
     || ASSIGNED '''or''' STARTED || - (''ignored'') || In Progress ||
     || NEEDINFO '''or''' WAITING '''or''' SUSPENDED || - (''ignored'') ...
@@ -832,7 +832,7 @@
 
     >>> titles = ('Status', 'Resolution')
     >>> for line in tree.moinmoin_table(titles):
-    ...     print line
+    ...     print(line)
     Traceback (most recent call last):
     ...
     ValueError: Table of 3 columns needs 3 titles, but 2 given.
@@ -848,7 +848,7 @@
     ...     )
     Traceback (most recent call last):
     ...
-    TypeError: Result is not a member of BugTaskStatus: 'Not a BugTaskStatus'
+    TypeError: Result is not a member of BugTaskStatus: u'Not a BugTaskStatus'
 
 
 Getting the remote product from a remote bug
@@ -884,7 +884,7 @@
 
     >>> basic_external_bugtracker = ExternalBugTracker(
     ...     'http://example.com')
-    >>> print basic_external_bugtracker.getRemoteProduct(1)
+    >>> print(basic_external_bugtracker.getRemoteProduct(1))
     None
 
 
@@ -938,7 +938,7 @@
     ...     external_bugtracker,
     ...     unchecked_watches + watches_with_comments,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'0', u'1', u'5', u'6', u'7']
 
 Previously-checked bug watches that need updating will only be included if
@@ -963,14 +963,14 @@
     ...     external_bugtracker,
     ...     unchecked_watches + watches_with_comments + old_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'0', u'1', u'5', u'6', u'7']
 
 The old IDs that aren't checked aren't included in the unmodified_remote_ids
 list, since they still need checking and shouldn't be marked as having been
 checked already.
 
-    >>> print sorted(ids['unmodified_remote_ids'])
+    >>> print(sorted(ids['unmodified_remote_ids']))
     []
 
 However, if there's room in the batch, old IDs that need checking will
@@ -981,7 +981,7 @@
     ...     external_bugtracker,
     ...     unchecked_watches + watches_with_comments + old_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8']
 
 If there's no batch_size set, all the bugs that should be checked are
@@ -992,7 +992,7 @@
     ...     external_bugtracker,
     ...     unchecked_watches + watches_with_comments + old_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9']
 
 
@@ -1013,7 +1013,7 @@
     ...     unchecked_watches + watches_with_comments + old_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now,
     ...     batch_size=2)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'5', u'6']
 
 If the batch_size parameter is set to None (the default value), the
@@ -1024,7 +1024,7 @@
     ...     unchecked_watches + watches_with_comments + old_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now,
     ...     batch_size=None)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'0', u'1', u'5', u'6', u'7']
 
 _getRemoteIdsToCheck() will interpret a batch_size parameter of 0 as an
@@ -1039,7 +1039,7 @@
     ...     unchecked_watches + watches_with_comments + old_watches,
     ...     external_bugtracker.getCurrentDBTime(), utc_now,
     ...     batch_size=BATCH_SIZE_UNLIMITED)
-    >>> print sorted(ids['remote_ids_to_check'])
+    >>> print(sorted(ids['remote_ids_to_check']))
     [u'0', u'1', u'2', u'3', u'4', u'5', u'6', u'7', u'8', u'9']
 
 batch_size can be passed to _getRemoteIdsToCheck() via updateBugWatches(),

=== modified file 'lib/lp/bugs/doc/filebug-data-parser.txt'
--- lib/lp/bugs/doc/filebug-data-parser.txt	2012-02-21 22:46:28 +0000
+++ lib/lp/bugs/doc/filebug-data-parser.txt	2018-06-30 11:02:51 +0000
@@ -12,9 +12,9 @@
 to be added to the bug report. The information is passed as a file
 object to the constructor.
 
-    >>> from StringIO import StringIO
+    >>> from io import BytesIO
     >>> from lp.bugs.utilities.filebugdataparser import FileBugDataParser
-    >>> parser = FileBugDataParser(StringIO('123456789'))
+    >>> parser = FileBugDataParser(BytesIO(b'123456789'))
 
 To make parsing easier and more efficient, it has a buffer where it
 stores the next few bytes of the file. To begin with, it's empty.
@@ -31,7 +31,7 @@
 There is helper method, _consumeBytes(), which will read from the file
 until a certain delimiter string is encountered.
 
-    >>> parser._consumeBytes('4')
+    >>> parser._consumeBytes(b'4')
     '1234'
 
 In order to find the delimiter string, it had to read '123456' into
@@ -43,7 +43,7 @@
 
 The delimiter string isn't limited to one character.
 
-    >>> parser._consumeBytes('67')
+    >>> parser._consumeBytes(b'67')
     '567'
 
     >>> parser._buffer
@@ -52,16 +52,16 @@
 If the delimiter isn't found in the file, the rest of the file is
 returned.
 
-    >>> parser._consumeBytes('0')
+    >>> parser._consumeBytes(b'0')
     '89'
     >>> parser._buffer
     ''
 
 Subsequent reads will result in the empty string.
 
-    >>> parser._consumeBytes('0')
+    >>> parser._consumeBytes(b'0')
     ''
-    >>> parser._consumeBytes('0')
+    >>> parser._consumeBytes(b'0')
     ''
 
 
@@ -69,7 +69,7 @@
 
 readLine() is a helper method to read a single line of the file.
 
-    >>> parser = FileBugDataParser(StringIO('123\n456\n789'))
+    >>> parser = FileBugDataParser(BytesIO(b'123\n456\n789'))
     >>> parser.readLine()
     '123\n'
     >>> parser.readLine()
@@ -92,7 +92,7 @@
 readHeaders() reads the headers of a MIME message. It reads all the
 headers, untils it sees a blank line.
 
-    >>> msg = """Header: value
+    >>> msg = b"""Header: value
     ... Space-Folded-Header: this header
     ...  is folded with a space.
     ... Tab-Folded-Header: this header
@@ -101,7 +101,7 @@
     ...
     ... Not-A-Header: not-a-value
     ... """
-    >>> parser = FileBugDataParser(StringIO(msg))
+    >>> parser = FileBugDataParser(BytesIO(msg))
     >>> headers = parser.readHeaders()
     >>> headers['Header']
     'value'
@@ -171,7 +171,7 @@
 
     >>> data = FileBugData()
     >>> parser._setDataFromHeaders(data, {'Private': 'not-valid'})
-    >>> print data.private
+    >>> print(data.private)
     None
 
 
@@ -224,11 +224,11 @@
 
     >>> used_parsers = []
     >>> def parse_message(message):
-    ...     parser = FileBugDataParser(StringIO(message))
+    ...     parser = FileBugDataParser(BytesIO(message))
     ...     used_parsers.append(parser)
     ...     return parser.parse()
 
-    >>> debug_data = """MIME-Version: 1.0
+    >>> debug_data = b"""MIME-Version: 1.0
     ... Content-type: multipart/mixed; boundary=boundary
     ...
     ... --boundary
@@ -248,13 +248,13 @@
 
 The text can also be base64 decoded.
 
-    >>> encoded_text = """VGhpcyBzaG91bGQgYmUgYWRkZWQgdG8g
+    >>> encoded_text = b"""VGhpcyBzaG91bGQgYmUgYWRkZWQgdG8g
     ... dGhlIGRlc2NyaXB0aW9uLgoKQW5vdGhl
     ... ciBsaW5lLg=="""
     >>> encoded_text.decode('base64')
     'This should be added to the description.\n\nAnother line.'
 
-    >>> debug_data = """MIME-Version: 1.0
+    >>> debug_data = b"""MIME-Version: 1.0
     ... Content-type: multipart/mixed; boundary=boundary
     ...
     ... --boundary
@@ -277,7 +277,7 @@
 to the bug. The comments are simple ext strings, accessible through the
 comments attribute.
 
-    >>> debug_data = """MIME-Version: 1.0
+    >>> debug_data = b"""MIME-Version: 1.0
     ... Content-type: multipart/mixed; boundary=boundary
     ...
     ... --boundary
@@ -317,7 +317,7 @@
 will get added as attachments to the bug. The attachment description can
 be specified using a Content-description header, but it's not required.
 
-    >>> debug_data = """MIME-Version: 1.0
+    >>> debug_data = b"""MIME-Version: 1.0
     ... Content-type: multipart/mixed; boundary=boundary
     ...
     ... --boundary
@@ -381,8 +381,8 @@
 
 Binary files are base64 encoded. They are decoded automatically.
 
-    >>> binary_data = '\n'.join(['\x00'*5, '\x01'*5])
-    >>> debug_data = """MIME-Version: 1.0
+    >>> binary_data = b'\n'.join([b'\x00'*5, b'\x01'*5])
+    >>> debug_data = b"""MIME-Version: 1.0
     ... Content-type: multipart/mixed; boundary=boundary
     ...
     ... --boundary
@@ -411,7 +411,7 @@
 don't care about giving the user a good error message, since the format
 is well-known.
 
-    >>> debug_data = """MIME-Version: 1.0
+    >>> debug_data = b"""MIME-Version: 1.0
     ... Content-type: multipart/mixed; boundary=boundary
     ...
     ... --boundary

=== modified file 'lib/lp/bugs/doc/initial-bug-contacts.txt'
--- lib/lp/bugs/doc/initial-bug-contacts.txt	2015-09-11 12:20:23 +0000
+++ lib/lp/bugs/doc/initial-bug-contacts.txt	2018-06-30 11:02:51 +0000
@@ -112,7 +112,7 @@
 
     >>> names = subscriber_names(bug_one_in_ubuntu_firefox.bug)
     >>> for name in names:
-    ...     print name
+    ...     print(name)
     Foo Bar
     Mark Shuttleworth
     Sample Person
@@ -178,10 +178,10 @@
     1
 
     >>> from_addr, to_addr, raw_message = test_emails.pop()
-    >>> print from_addr
+    >>> print(from_addr)
     bounces@xxxxxxxxxxxxx
 
-    >>> print to_addr
+    >>> print(to_addr)
     ['daf@xxxxxxxxxxxxx']
 
     >>> msg = email.message_from_string(raw_message)
@@ -197,7 +197,7 @@
     >>> msg['Subject']
     '[Bug 1] [NEW] Firefox does not support SVG'
 
-    >>> print msg.get_payload(decode=True)
+    >>> print(msg.get_payload(decode=True))
     You have been subscribed to a public bug:
     <BLANKLINE>
     Firefox needs to support embedded SVG images, now that the standard has
@@ -231,7 +231,7 @@
 Since the reporter didn't do anything to trigger this change, the bug
 address is used as the From address.
 
-    >>> print msg['From']
+    >>> print(msg['From'])
     Launchpad Bug Tracker <1@xxxxxxxxxxxxxxxxxx>
 
     >>> stub.test_emails = []
@@ -255,7 +255,7 @@
 
     >>> names = subscriber_names(bug_one_in_ubuntu_firefox.bug)
     >>> for name in names:
-    ...     print name
+    ...     print(name)
     Mark Shuttleworth
     Sample Person
     Steve Alexander
@@ -306,10 +306,10 @@
 Then we'll reassign bug #2 in Ubuntu to be in Firefox:
 
     >>> bug_two_in_ubuntu = getUtility(IBugTaskSet).get(3)
-    >>> print bug_two_in_ubuntu.bug.id
+    >>> print(bug_two_in_ubuntu.bug.id)
     2
 
-    >>> print bug_two_in_ubuntu.product.name
+    >>> print(bug_two_in_ubuntu.product.name)
     tomcat
 
     >>> sorted(
@@ -360,7 +360,7 @@
 supervisor:
 
     >>> for team in view.user.getAdministratedTeams():
-    ...        print team.displayname
+    ...        print(team.displayname)
     Landscape Developers
     Launchpad Users
 

=== modified file 'lib/lp/bugs/doc/malone-karma.txt'
--- lib/lp/bugs/doc/malone-karma.txt	2016-06-25 07:56:09 +0000
+++ lib/lp/bugs/doc/malone-karma.txt	2018-06-30 11:02:51 +0000
@@ -103,7 +103,7 @@
     Karma added: action=bugfixed, product=ufo
 
     >>> for karma in assignee.latestKarma():
-    ...     print karma.action.name
+    ...     print(karma.action.name)
     bugfixed
 
 Reject a bug task:
@@ -142,7 +142,7 @@
     ...     old_bugtask = Snapshot(bugtask, providing=IBugTask)
     ...     bugtask.transitionToImportance(
     ...         importance, getUtility(ILaunchBag).user)
-    ...     print importance.name
+    ...     print(importance.name)
     ...     notify(ObjectModifiedEvent(bugtask, old_bugtask, ['importance']))
     UNKNOWN
     Karma added: action=bugtaskimportancechanged, distribution=debian

=== modified file 'lib/lp/bugs/doc/malone-xmlrpc.txt'
--- lib/lp/bugs/doc/malone-xmlrpc.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/bugs/doc/malone-xmlrpc.txt	2018-06-30 11:02:51 +0000
@@ -54,7 +54,7 @@
     >>> from lp.bugs.interfaces.bug import IBug
 
     >>> def on_created_event(obj, event):
-    ...     print "ObjectCreatedEvent: %r" % obj
+    ...     print("ObjectCreatedEvent: %r" % obj)
 
     >>> on_created_listener = TestEventListener(
     ...     IBug, IObjectCreatedEvent, on_created_event)
@@ -71,7 +71,7 @@
     ...     product='firefox', summary='the summary', comment='the comment')
     >>> bug_url = filebug_api.filebug(params)
     ObjectCreatedEvent: <Bug ...>
-    >>> print bug_url
+    >>> print(bug_url)
     http://bugs.launchpad.dev/bugs/...
 
     >>> from zope.component import getUtility
@@ -80,16 +80,16 @@
     >>> bugset = getUtility(IBugSet)
     >>> bug = bugset.get(get_bug_id_from_url(bug_url))
 
-    >>> print bug.title
+    >>> print(bug.title)
     the summary
-    >>> print bug.description
+    >>> print(bug.description)
     the comment
-    >>> print bug.owner.name
+    >>> print(bug.owner.name)
     name12
 
     >>> firefox_bug = bug.bugtasks[0]
 
-    >>> print firefox_bug.product.name
+    >>> print(firefox_bug.product.name)
     firefox
 
 Reporting a distro bug.
@@ -98,21 +98,21 @@
     ...     distro='ubuntu', summary='another bug', comment='another comment')
     >>> bug_url = filebug_api.filebug(params)
     ObjectCreatedEvent: <Bug ...>
-    >>> print bug_url
+    >>> print(bug_url)
     http://bugs.launchpad.dev/bugs/...
 
     >>> bug = bugset.get(get_bug_id_from_url(bug_url))
 
-    >>> print bug.title
+    >>> print(bug.title)
     another bug
-    >>> print bug.description
+    >>> print(bug.description)
     another comment
-    >>> print bug.owner.name
+    >>> print(bug.owner.name)
     name12
 
     >>> ubuntu_bug = bug.bugtasks[0]
 
-    >>> print ubuntu_bug.distribution.name
+    >>> print(ubuntu_bug.distribution.name)
     ubuntu
     >>> ubuntu_bug.sourcepackagename is None
     True
@@ -125,15 +125,15 @@
     ...     subscribers=["no-priv@xxxxxxxxxxxxx"])
     >>> bug_url = filebug_api.filebug(params)
     ObjectCreatedEvent: <Bug ...>
-    >>> print bug_url
+    >>> print(bug_url)
     http://bugs.launchpad.dev/bugs/...
 
     >>> login('test@xxxxxxxxxxxxx')
     >>> bug = bugset.get(get_bug_id_from_url(bug_url))
 
-    >>> print bug.title
+    >>> print(bug.title)
     email is cool
-    >>> print bug.description
+    >>> print(bug.description)
     email is nice
     >>> bug.security_related
     True
@@ -146,9 +146,9 @@
 
     >>> evolution_bug = bug.bugtasks[0]
 
-    >>> print evolution_bug.distribution.name
+    >>> print(evolution_bug.distribution.name)
     ubuntu
-    >>> print evolution_bug.sourcepackagename.name
+    >>> print(evolution_bug.sourcepackagename.name)
     evolution
 
 
@@ -271,7 +271,7 @@
 
 The LoginToken generated will be of the LoginTokenType BUGTRACKER.
 
-    >>> print token.tokentype.title
+    >>> print(token.tokentype.title)
     Launchpad is authenticating itself with a remote bug tracker.
 
 These requests are all handled by the private xml-rpc server.
@@ -285,5 +285,5 @@
     >>> token
     <LoginToken at ...>
 
-    >>> print token.tokentype.title
+    >>> print(token.tokentype.title)
     Launchpad is authenticating itself with a remote bug tracker.

=== modified file 'lib/lp/bugs/doc/official-bug-tags.txt'
--- lib/lp/bugs/doc/official-bug-tags.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/bugs/doc/official-bug-tags.txt	2018-06-30 11:02:51 +0000
@@ -62,7 +62,7 @@
     ...     OfficialBugTag, OfficialBugTag.distribution==ubuntu)
     >>> result_set = result_set.order_by(OfficialBugTag.tag)
     >>> for tag in result_set:
-    ...     print tag.tag
+    ...     print(tag.tag)
     bar
     foo
 
@@ -70,7 +70,7 @@
     >>> result_set = store.find(
     ...     OfficialBugTag, OfficialBugTag.distribution==ubuntu)
     >>> for tag in result_set:
-    ...     print tag.tag
+    ...     print(tag.tag)
     bar
 
     >>> login('test@xxxxxxxxxxxxx')
@@ -78,13 +78,13 @@
     >>> result_set = store.find(
     ...     OfficialBugTag, OfficialBugTag.product==firefox)
     >>> for tag in result_set:
-    ...     print tag.tag
+    ...     print(tag.tag)
     foo
 
     >>> firefox.removeOfficialBugTag(u'foo')
     >>> result_set = store.find(
     ...     OfficialBugTag, OfficialBugTag.product==firefox)
-    >>> print result_set.count()
+    >>> print(result_set.count())
     0
 
     >>> transaction.commit()
@@ -98,7 +98,7 @@
     ...     OfficialBugTag, OfficialBugTag.distribution==ubuntu)
     >>> result_set = result_set.order_by(OfficialBugTag.tag)
     >>> for tag in result_set:
-    ...     print tag.tag
+    ...     print(tag.tag)
     bar
 
 Similary, deleting an not-existent tag does not lead to an error, but
@@ -108,7 +108,7 @@
     >>> result_set = store.find(
     ...     OfficialBugTag, OfficialBugTag.distribution==ubuntu)
     >>> for tag in result_set:
-    ...     print tag.tag
+    ...     print(tag.tag)
     bar
 
 Ordinary users cannot add and remove official bug tags.

=== modified file 'lib/lp/bugs/doc/product-update-remote-product-script.txt'
--- lib/lp/bugs/doc/product-update-remote-product-script.txt	2010-05-18 22:36:20 +0000
+++ lib/lp/bugs/doc/product-update-remote-product-script.txt	2018-06-30 11:02:51 +0000
@@ -13,7 +13,7 @@
     >>> process.returncode
     0
 
-    >>> print err
+    >>> print(err)
     INFO    Creating lockfile: /var/lock/launchpad-updateremoteproduct.lock
     INFO    0 projects using BUGZILLA needing updating.
     ...

=== modified file 'lib/lp/bugs/doc/product-update-remote-product.txt'
--- lib/lp/bugs/doc/product-update-remote-product.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/bugs/doc/product-update-remote-product.txt	2018-06-30 11:02:51 +0000
@@ -42,7 +42,7 @@
     >>> class FakeExternalBugTracker:
     ...
     ...     def initializeRemoteBugDB(self, bug_ids):
-    ...         print "Initializing DB for bugs: %s." % bug_ids
+    ...         print("Initializing DB for bugs: %s." % bug_ids)
     ...
     ...     def getRemoteProduct(self, remote_bug):
     ...         return 'product-for-bug-%s' % remote_bug
@@ -115,9 +115,9 @@
     >>> list(bugzilla_product.getLinkedBugWatches())
     []
     >>> updater.updateByBugTrackerType(BugTrackerType.RT)
-    >>> print bugzilla_product.remote_product
+    >>> print(bugzilla_product.remote_product)
     None
-    >>> print rt_product.remote_product
+    >>> print(rt_product.remote_product)
     None
 
 
@@ -147,10 +147,10 @@
     >>> updater.updateByBugTrackerType(BugTrackerType.RT)
     Initializing DB for bugs: [u'84'].
 
-    >>> print rt_product.remote_product
+    >>> print(rt_product.remote_product)
     product-for-bug-84
 
-    >>> print bugzilla_product.remote_product
+    >>> print(bugzilla_product.remote_product)
     None
 
 
@@ -173,7 +173,7 @@
     >>> updater = NoNetworkRemoteProductUpdater(
     ...     FakeTransaction(), BufferLogger())
     >>> updater.updateByBugTrackerType(BugTrackerType.RT)
-    >>> print rt_product.remote_product
+    >>> print(rt_product.remote_product)
     already-set
 
 

=== modified file 'lib/lp/bugs/doc/products-with-no-remote-product.txt'
--- lib/lp/bugs/doc/products-with-no-remote-product.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/bugs/doc/products-with-no-remote-product.txt	2018-06-30 11:02:51 +0000
@@ -20,7 +20,7 @@
     >>> from lp.testing.factory import LaunchpadObjectFactory
     >>> factory = LaunchpadObjectFactory()
     >>> product = factory.makeProduct(name=u'no-remote-product')
-    >>> print product.remote_product
+    >>> print(product.remote_product)
     None
 
     >>> products = getUtility(IProductSet).getProductsWithNoneRemoteProduct()

=== modified file 'lib/lp/bugs/doc/sourceforge-remote-products.txt'
--- lib/lp/bugs/doc/sourceforge-remote-products.txt	2018-05-31 12:47:31 +0000
+++ lib/lp/bugs/doc/sourceforge-remote-products.txt	2018-06-30 11:02:51 +0000
@@ -16,7 +16,7 @@
     >>> products = getUtility(
     ...     IProductSet).getSFLinkedProductsWithNoneRemoteProduct()
 
-    >>> print products.count()
+    >>> print(products.count())
     0
 
 If we add a Product and link it to a SourceForge project,
@@ -34,7 +34,7 @@
     ...     IProductSet).getSFLinkedProductsWithNoneRemoteProduct()
 
     >>> for product in products:
-    ...     print product.name, product.sourceforgeproject
+    ...     print(product.name, product.sourceforgeproject)
     my-first-product fronobulator
 
 Define some request mocks so that we don't try to access SourceForge.
@@ -101,7 +101,7 @@
     Got page /projects/fronobulator
     Got page /tracker/?group_id=5570
 
-    >>> print remote_product
+    >>> print(remote_product)
     5570&105570
 
 If an error is raised when trying to fetch the project pages from the
@@ -131,7 +131,7 @@
 its remote_product set.
 
     >>> product_1 = getUtility(IProductSet).getByName('my-first-product')
-    >>> print product_1.remote_product
+    >>> print(product_1.remote_product)
     5570&105570
 
 There are no other SourceForge-linked products that have no remote product.
@@ -139,7 +139,7 @@
     >>> products = getUtility(
     ...     IProductSet).getSFLinkedProductsWithNoneRemoteProduct()
 
-    >>> print products.count()
+    >>> print(products.count())
     0
 
 
@@ -161,7 +161,7 @@
     >>> process.returncode
     0
 
-    >>> print err
+    >>> print(err)
     DEBUG   ...
     INFO    No Products to update.
     INFO    Time for this run: ... seconds.

=== modified file 'lib/lp/bugs/doc/structural-subscriptions.txt'
--- lib/lp/bugs/doc/structural-subscriptions.txt	2011-05-05 05:46:41 +0000
+++ lib/lp/bugs/doc/structural-subscriptions.txt	2018-06-30 11:02:51 +0000
@@ -53,29 +53,29 @@
 A target's parent can be retrieved using the
 `parent_subscription_target` property.
 
-    >>> print evolution_package.parent_subscription_target.displayname
+    >>> print(evolution_package.parent_subscription_target.displayname)
     Ubuntu
-    >>> print ubuntu.parent_subscription_target
+    >>> print(ubuntu.parent_subscription_target)
     None
-    >>> print firefox.parent_subscription_target.displayname
+    >>> print(firefox.parent_subscription_target.displayname)
     The Mozilla Project
 
     >>> ff_milestone = firefox.getMilestone('1.0')
     >>> ff_milestone.parent_subscription_target == firefox
     True
-    >>> print ff_milestone.parent_subscription_target.displayname
+    >>> print(ff_milestone.parent_subscription_target.displayname)
     Mozilla Firefox
 
     >>> ff_trunk = firefox.getSeries('trunk')
     >>> ff_trunk.parent_subscription_target == firefox
     True
-    >>> print ff_trunk.parent_subscription_target.displayname
+    >>> print(ff_trunk.parent_subscription_target.displayname)
     Mozilla Firefox
 
     >>> warty = ubuntu.getSeries('warty')
     >>> warty.parent_subscription_target == ubuntu
     True
-    >>> print warty.parent_subscription_target.displayname
+    >>> print(warty.parent_subscription_target.displayname)
     Ubuntu
 
 When notifying subscribers of bug activity, both subscribers to the
@@ -88,9 +88,9 @@
 Structural subscription targets have a `target_type_display` attribute, which
 can be used to refer to them in display.
 
-    >>> print firefox.target_type_display
+    >>> print(firefox.target_type_display)
     project
-    >>> print evolution_package.target_type_display
+    >>> print(evolution_package.target_type_display)
     package
-    >>> print ff_milestone.target_type_display
+    >>> print(ff_milestone.target_type_display)
     milestone

=== modified file 'lib/lp/bugs/doc/treelookup.txt'
--- lib/lp/bugs/doc/treelookup.txt	2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/doc/treelookup.txt	2018-06-30 11:02:51 +0000
@@ -89,28 +89,28 @@
   >>> invalid_tree._verify()
   Traceback (most recent call last):
   ...
-  TypeError: Not a LookupBranch: 'Greenland'
+  TypeError: Not a LookupBranch: u'Greenland'
 
 
 == Searching ==
 
 Just call `tree.find`.
 
-  >>> tree.find('Snack', 'Banana')
-  'Good'
+  >>> print(tree.find('Snack', 'Banana'))
+  Good
 
 If you specify more keys than you need to reach a leaf, you still get
 the result.
 
-  >>> tree.find('Snack', 'Banana', 'Big', 'Yellow', 'Taxi')
-  'Good'
+  >>> print(tree.find('Snack', 'Banana', 'Big', 'Yellow', 'Taxi'))
+  Good
 
 But an exception is raised if it does not reach a leaf.
 
   >>> tree.find('Snack')
   Traceback (most recent call last):
   ...
-  KeyError: 'Snack'
+  KeyError: u'Snack'
 
 
 == Development ==
@@ -118,34 +118,34 @@
 `LookupTree` makes development easy, because `describe` gives a
 complete description of the tree you've created.
 
-  >>> print tree.describe()
+  >>> print(tree.describe())
   tree(
       branch(Snack => tree(
-          branch('Mars Bar', Snickers => 'Bad')
-          branch(Apple, Banana => 'Good')
+          branch(u'Mars Bar', Snickers => u'Bad')
+          branch(Apple, Banana => u'Good')
           ))
       branch(Lunch, Dinner => tree(
-          branch('Fish and chips', "Penne all'arrabbiata" => 'Nice')
-          branch('Raw liver' => 'Not so nice')
+          branch(u'Fish and chips', u"Penne all'arrabbiata" => u'Nice')
+          branch(u'Raw liver' => u'Not so nice')
           ))
-      branch(* => 'Make up your mind!')
+      branch(* => u'Make up your mind!')
       )
 
 We can also see that the result of constructing a new lookup using an
 existing one is the same as if we had constructed it independently.
 
-  >>> print breakfast_tree.describe()
+  >>> print(breakfast_tree.describe())
   tree(
-      branch(Breakfast => 'Corn flakes')
+      branch(Breakfast => u'Corn flakes')
       branch(Snack => tree(
-          branch('Mars Bar', Snickers => 'Bad')
-          branch(Apple, Banana => 'Good')
+          branch(u'Mars Bar', Snickers => u'Bad')
+          branch(Apple, Banana => u'Good')
           ))
       branch(Lunch, Dinner => tree(
-          branch('Fish and chips', "Penne all'arrabbiata" => 'Nice')
-          branch('Raw liver' => 'Not so nice')
+          branch(u'Fish and chips', u"Penne all'arrabbiata" => u'Nice')
+          branch(u'Raw liver' => u'Not so nice')
           ))
-      branch(* => 'Make up your mind!')
+      branch(* => u'Make up your mind!')
       )
 
 Simple keys are shown without quotes, to aid readability, and default
@@ -171,12 +171,12 @@
   ...     ('Lunch', 'Dinner', 'Soup'),
   ...     ('Eat more fruit and veg',),
   ...     )
-  >>> print pruned_tree.describe()
+  >>> print(pruned_tree.describe())
   tree(
-      branch(Snack => 'Crisps')
-      branch(Lunch => 'Bread')
-      branch(Dinner => 'Soup')
-      branch(* => 'Eat more fruit and veg')
+      branch(Snack => u'Crisps')
+      branch(Lunch => u'Bread')
+      branch(Dinner => u'Soup')
+      branch(* => u'Eat more fruit and veg')
       )
 
 
@@ -195,10 +195,11 @@
 
   >>> for elems in tree.flatten():
   ...     path, result = elems[:-1], elems[-1]
-  ...     print ' => '.join(
-  ...         [repr(node.keys) for node in path] + [repr(result)])
-  ('Snack',) => ('Mars Bar', 'Snickers') => 'Bad'
-  ('Snack',) => ('Apple', 'Banana') => 'Good'
-  ('Lunch', 'Dinner') => ('Fish and chips', "Penne all'arrabbiata") => 'Nice'
-  ('Lunch', 'Dinner') => ('Raw liver',) => 'Not so nice'
-  () => 'Make up your mind!'
+  ...     print(' => '.join(
+  ...         [repr(node.keys) for node in path] + [repr(result)]))
+  (u'Snack',) => (u'Mars Bar', u'Snickers') => u'Bad'
+  (u'Snack',) => (u'Apple', u'Banana') => u'Good'
+  (u'Lunch', u'Dinner') =>
+      (u'Fish and chips', u"Penne all'arrabbiata") => u'Nice'
+  (u'Lunch', u'Dinner') => (u'Raw liver',) => u'Not so nice'
+  () => u'Make up your mind!'

=== modified file 'lib/lp/bugs/doc/vocabularies.txt'
--- lib/lp/bugs/doc/vocabularies.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/bugs/doc/vocabularies.txt	2018-06-30 11:02:51 +0000
@@ -72,7 +72,7 @@
     >>> len(using_malone_vocabulary)
     2
     >>> for term in using_malone_vocabulary:
-    ...     print term.token, term.value.displayname, term.title
+    ...     print(term.token, term.value.displayname, term.title)
     gentoo Gentoo Gentoo
     ubuntu Ubuntu Ubuntu
 
@@ -85,7 +85,7 @@
     False
 
     >>> term = using_malone_vocabulary.getTerm(ubuntu)
-    >>> print term.token, term.value.displayname, term.title
+    >>> print(term.token, term.value.displayname, term.title)
     ubuntu Ubuntu Ubuntu
 
     >>> term = using_malone_vocabulary.getTerm(debian)
@@ -94,7 +94,7 @@
     LookupError:...
 
     >>> term = using_malone_vocabulary.getTermByToken('ubuntu')
-    >>> print term.token, term.value.displayname, term.title
+    >>> print(term.token, term.value.displayname, term.title)
     ubuntu Ubuntu Ubuntu
 
     >>> term = using_malone_vocabulary.getTermByToken('debian')
@@ -121,7 +121,7 @@
 Firefox has the following series:
 
     >>> for series in firefox.series:
-    ...     print series.name
+    ...     print(series.name)
     1.0
     trunk
 
@@ -132,7 +132,7 @@
 
     >>> bug_one = getUtility(IBugSet).get(1)
     >>> for bugtask in bug_one.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     mozilla-firefox (Ubuntu)
     mozilla-firefox (Debian)
@@ -140,7 +140,7 @@
 It has however been nominated for 1.0:
 
     >>> for nomination in bug_one.getNominations(firefox):
-    ...     print nomination.target.name
+    ...     print(nomination.target.name)
     1.0
 
 This means that if we iterate through the vocabulary with bug one, only
@@ -152,18 +152,18 @@
     >>> series_vocabulary = vocabulary_registry.get(
     ...     firefox_bug_one, 'BugNominatableSeries')
     >>> for term in series_vocabulary:
-    ...     print "%s: %s" % (term.token, term.title)
+    ...     print("%s: %s" % (term.token, term.title))
     trunk: Trunk
 
 No series is targeted or nominated on bug 4:
 
     >>> bug_four = getUtility(IBugSet).get(4)
     >>> for bugtask in bug_four.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
 
     >>> for nomination in bug_four.getNominations(firefox):
-    ...     print nomination.target.name
+    ...     print(nomination.target.name)
 
 So if we give bug four to the vocabulary, all series will be returned:
 
@@ -173,7 +173,7 @@
     >>> series_vocabulary = vocabulary_registry.get(
     ...     firefox_bug_four, 'BugNominatableSeries')
     >>> for term in series_vocabulary:
-    ...     print "%s: %s" % (term.token, term.title)
+    ...     print("%s: %s" % (term.token, term.title))
     1.0: 1.0
     trunk: Trunk
 
@@ -186,13 +186,13 @@
 
     >>> bug_one = getUtility(IBugSet).get(1)
     >>> for bugtask in bug_one.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     mozilla-firefox (Ubuntu)
     mozilla-firefox (Debian)
 
     >>> for nomination in bug_one.getNominations(ubuntu):
-    ...     print nomination.target.name
+    ...     print(nomination.target.name)
     hoary
 
 So Hoary isn't included in the vocabulary:
@@ -203,7 +203,7 @@
     >>> series_vocabulary = vocabulary_registry.get(
     ...     ubuntu_bug_one, 'BugNominatableSeries')
     >>> for term in series_vocabulary:
-    ...     print "%s: %s" % (term.token, term.title)
+    ...     print("%s: %s" % (term.token, term.title))
     breezy-autotest: Breezy-autotest
     grumpy: Grumpy
     warty: Warty
@@ -212,7 +212,7 @@
 
     >>> bug_two = getUtility(IBugSet).get(2)
     >>> for bugtask in bug_two.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Tomcat
     Ubuntu
     Ubuntu Hoary
@@ -220,7 +220,7 @@
     mozilla-firefox (Debian Woody)
 
     >>> for nomination in bug_two.getNominations(ubuntu):
-    ...     print nomination.target.name
+    ...     print(nomination.target.name)
     hoary
 
     >>> ubuntu_bug_two = bug_two.bugtasks[1]
@@ -229,7 +229,7 @@
     >>> series_vocabulary = vocabulary_registry.get(
     ...     ubuntu_bug_two, 'BugNominatableSeries')
     >>> for term in series_vocabulary:
-    ...     print "%s: %s" % (term.token, term.title)
+    ...     print("%s: %s" % (term.token, term.title))
     breezy-autotest: Breezy-autotest
     grumpy: Grumpy
     warty: Warty
@@ -257,14 +257,14 @@
 
     >>> mozilla_project = getUtility(IProjectGroupSet).getByName('mozilla')
     >>> for product in mozilla_project.products:
-    ...     print "%s: %s" % (product.name, product.bug_tracking_usage.name)
+    ...     print("%s: %s" % (product.name, product.bug_tracking_usage.name))
     firefox: LAUNCHPAD
     thunderbird: UNKNOWN
 
     >>> mozilla_products_vocabulary = vocabulary_registry.get(
     ...     mozilla_project,'ProjectProductsUsingMalone')
     >>> for term in mozilla_products_vocabulary:
-    ...     print "%s: %s" %(term.token, term.title)
+    ...     print("%s: %s" %(term.token, term.title))
     firefox: Mozilla Firefox
 
 
@@ -285,7 +285,7 @@
     >>> bugtask = bug_one.bugtasks[0]
     >>> vocab = vocabulary_registry.get(bugtask, "BugWatch")
     >>> for term in vocab:
-    ...     print term.title
+    ...     print(term.title)
     The Mozilla.org Bug Tracker <a...>#123543</a>
     The Mozilla.org Bug Tracker <a...>#2000</a>
     The Mozilla.org Bug Tracker <a...>#42</a>
@@ -309,7 +309,7 @@
     >>> bugtask = bug_twelve.bugtasks[0]
     >>> vocab = vocabulary_registry.get(bugtask, "BugWatch")
     >>> for term in vocab:
-    ...     print term.title
+    ...     print(term.title)
     Email bugtracker &lt;<a...>bugs@xxxxxxxxxxx</a>&gt;
 
 Additionally, if the bug tracker's title contains the bug tracker's
@@ -320,7 +320,7 @@
     ...         email_bugtracker.baseurl,))
 
     >>> for term in vocab:
-    ...     print term.title
+    ...     print(term.title)
     Lionel Richtea (<a...>mailto:bugs@xxxxxxxxxxx</a>)
 
 When there is no logged-in user, the title is much different. The
@@ -330,5 +330,5 @@
     >>> login(ANONYMOUS)
 
     >>> for term in vocab:
-    ...     print term.title
+    ...     print(term.title)
     Lionel Richtea (mailto:&lt;email address hidden&gt;)

=== modified file 'lib/lp/bugs/tests/bugs-emailinterface.txt'
--- lib/lp/bugs/tests/bugs-emailinterface.txt	2016-02-08 12:20:20 +0000
+++ lib/lp/bugs/tests/bugs-emailinterface.txt	2018-06-30 11:02:51 +0000
@@ -95,9 +95,9 @@
     ...     return latest_notification.bug
     >>> bug = get_latest_added_bug()
 
-    >>> print bug.title
+    >>> print(bug.title)
     A bug in Firefox
-    >>> print bug.description
+    >>> print(bug.description)
     There is a bug in Firefox.
     <BLANKLINE>
      affects firefox
@@ -107,7 +107,7 @@
     >>> len(bug.bugtasks)
     1
     >>> upstream_task = bug.bugtasks[0]
-    >>> print upstream_task.product.name
+    >>> print(upstream_task.product.name)
     firefox
 
 And the entire body of the email was added as a comment:
@@ -115,22 +115,22 @@
     >>> bug.messages.count()
     1
     >>> comment = bug.messages[0]
-    >>> print comment.title
+    >>> print(comment.title)
     A bug in Firefox
-    >>> print comment.text_contents
+    >>> print(comment.text_contents)
     There is a bug in Firefox.
     <BLANKLINE>
      affects firefox
 
 The owner of the bug was set to the submitter:
 
-    >>> print bug.owner.displayname
+    >>> print(bug.owner.displayname)
     Foo Bar
 
 A notification was added:
 
     >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print bug_notification.message.owner.displayname
+    >>> print(bug_notification.message.owner.displayname)
     Foo Bar
 
     >>> bug_notification.message == bug.initial_message
@@ -163,19 +163,19 @@
     >>> process_email(submit_mail)
     >>> bug = get_latest_added_bug()
 
-    >>> print bug.title
+    >>> print(bug.title)
     A bug in Ubuntu's Mozilla package
 
     >>> distrotask = bug.bugtasks[0]
-    >>> print distrotask.distribution.name
+    >>> print(distrotask.distribution.name)
     ubuntu
-    >>> print distrotask.sourcepackagename.name
+    >>> print(distrotask.sourcepackagename.name)
     mozilla-firefox
 
 A notification was added:
 
     >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print bug_notification.message.owner.displayname
+    >>> print(bug_notification.message.owner.displayname)
     Sample Person
 
     >>> bug_notification.message == bug.initial_message
@@ -214,11 +214,11 @@
     >>> process_email(submit_mail)
     >>> bug = get_latest_added_bug()
 
-    >>> print bug.title
+    >>> print(bug.title)
     Affects many packages
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetname
+    ...     print(bugtask.bugtargetname)
     evolution
     firefox
     evolution (Debian)
@@ -272,7 +272,7 @@
     >>> added_message = getUtility(IMessageSet).get('<yada-yada-test1>')[0]
     >>> added_message in bug_one.messages
     True
-    >>> print bug_one.title
+    >>> print(bug_one.title)
     Better summary
 
 If the message doesn't have a Reference or In-Reply-To header, the
@@ -318,9 +318,9 @@
 
 And the summaries were changed:
 
-    >>> print bug_four.title
+    >>> print(bug_four.title)
     Changed summary
-    >>> print bug_five.title
+    >>> print(bug_five.title)
     Nicer summary
 
 The email handler requires that a bug be specified to be changed. If no
@@ -340,9 +340,9 @@
 
 This time, neither bug four or five were updated.
 
-    >>> print bug_four.title
+    >>> print(bug_four.title)
     Changed summary
-    >>> print bug_five.title
+    >>> print(bug_five.title)
     Nicer summary
 
 And the person sending the email has received an error message.
@@ -354,10 +354,10 @@
     ...     from_addr, to_addrs, raw_message = stub.test_emails[-1]
     ...     sent_msg = email.message_from_string(raw_message)
     ...     error_mail, original_mail = sent_msg.get_payload()
-    ...     print "Subject: %s" % sent_msg['Subject']
-    ...     print "To: %s" % ', '.join(to_addrs)
-    ...     print
-    ...     print error_mail.get_payload(decode=True)
+    ...     print("Subject: %s" % sent_msg['Subject'])
+    ...     print("To: %s" % ', '.join(to_addrs))
+    ...     print()
+    ...     print(error_mail.get_payload(decode=True))
 
     >>> print_latest_email()
     Subject: Submit Request Failure
@@ -431,7 +431,7 @@
     NotFoundError:...
 
     >>> bug_one = bugset.get(1)
-    >>> print bug_one.title
+    >>> print(bug_one.title)
     Better summary
 
 And an error message was sent to the Sample Person, telling them what's
@@ -466,7 +466,7 @@
     NotFoundError:...
 
     >>> bug_one = bugset.get(1)
-    >>> print bug_one.title
+    >>> print(bug_one.title)
     Better summary
 
 
@@ -576,13 +576,13 @@
 quotes. Example:
 
     >>> submit_commands(bug_four, 'summary "New summary"')
-    >>> print bug_four.title
+    >>> print(bug_four.title)
     New summary
 
 Whitespace will be preserved in the title:
 
     >>> submit_commands(bug_four, 'summary "New             summary"')
-    >>> print bug_four.title #doctest: -NORMALIZE_WHITESPACE
+    >>> print(bug_four.title) #doctest: -NORMALIZE_WHITESPACE
     New             summary
 
 If we omit the quotes, there will be an error:
@@ -615,7 +615,7 @@
 We will also add an attachment to the bug.
 
     >>> bug_attachment = bug_four.addAttachment(
-    ...     bug_four.owner, 'Attachment', 'No comment', 'test.txt')
+    ...     bug_four.owner, b'Attachment', 'No comment', 'test.txt')
 
     >>> submit_commands(bug_four, 'private yes')
     >>> bug_four.private
@@ -645,9 +645,9 @@
 
 The timestamp and user are cleared:
 
-    >>> print bug_four.date_made_private
+    >>> print(bug_four.date_made_private)
     None
-    >>> print bug_four.who_made_private
+    >>> print(bug_four.who_made_private)
     None
 
 Specifying something else than 'yes' or 'no' produces an error:
@@ -866,7 +866,7 @@
 
     >>> submit_commands(bug_four, 'tag foo bar foo bar')
     >>> for tag in bug_four.tags:
-    ...     print tag
+    ...     print(tag)
     bar
     foo
     layout-test
@@ -875,7 +875,7 @@
 
     >>> submit_commands(bug_four, 'tag -foo')
     >>> for tag in bug_four.tags:
-    ...     print tag
+    ...     print(tag)
     bar
     layout-test
 
@@ -935,7 +935,7 @@
 
     >>> submit_commands(bug_four, 'tag with-hyphen+period.')
     >>> for tag in bug_four.tags:
-    ...     print tag
+    ...     print(tag)
     bar
     layout-test
     with-hyphen+period.
@@ -1019,7 +1019,7 @@
     >>> bug = new_firefox_bug()
     >>> submit_commands(bug, 'cve CVE-1999-8979')
     >>> for cve in bug.cves:
-    ...     print cve.displayname
+    ...     print(cve.displayname)
     CVE-1999-8979
 
 If the CVE sequence can't be found, an error message is sent to the
@@ -1085,7 +1085,7 @@
 A notification was added:
 
     >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print bug_notification.message.text_contents
+    >>> print(bug_notification.message.text_contents)
     ** Also affects: debian
     ...
 
@@ -1107,24 +1107,24 @@
     >>> len(bug_four.bugtasks)
     2
     >>> debian_task = bug_four.bugtasks[-1]
-    >>> print debian_task.importance.name
+    >>> print(debian_task.importance.name)
     CRITICAL
-    >>> print debian_task.status.name
+    >>> print(debian_task.status.name)
     CONFIRMED
-    >>> print debian_task.assignee.displayname
+    >>> print(debian_task.assignee.displayname)
     Sample Person
 
 A milestone can be assigned to the current task.
 
     >>> firefox_task = [bugtask for bugtask in bug_four.bugtasks
     ...                 if bugtask.pillar.name == 'firefox'][0]
-    >>> print firefox_task.milestone
+    >>> print(firefox_task.milestone)
     None
     >>> submit_commands(bug_four, 'milestone 1.0')
-    >>> print firefox_task.milestone.name
+    >>> print(firefox_task.milestone.name)
     1.0
     >>> submit_commands(bug_four, 'milestone -')
-    >>> print firefox_task.milestone
+    >>> print(firefox_task.milestone)
     None
 
 Trying to set a milestone that does not exist elicits a helpful error
@@ -1153,12 +1153,12 @@
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.pillar.title
+    ...     print(bugtask.pillar.title)
     Mozilla Firefox
-    >>> print bug.bugtasks[0].milestone
+    >>> print(bug.bugtasks[0].milestone)
     None
     >>> submit_commands(bug, 'milestone 1.0')
-    >>> print bug.bugtasks[0].milestone
+    >>> print(bug.bugtasks[0].milestone)
     None
     >>> print_latest_email()
     Subject: Submit Request Failure
@@ -1211,7 +1211,7 @@
     >>> len(bug_four.bugtasks)
     2
     >>> debian_task = bug_four.bugtasks[-1]
-    >>> print debian_task.sourcepackagename.name
+    >>> print(debian_task.sourcepackagename.name)
     mozilla-firefox
 
 If we specify another source package in the same distribution, a new
@@ -1221,7 +1221,7 @@
     >>> len(bug_four.bugtasks)
     3
     >>> evolution_task = bug_four.bugtasks[-2]
-    >>> print evolution_task.sourcepackagename.name
+    >>> print(evolution_task.sourcepackagename.name)
     evolution
 
 It's also possible to add tasks for specific distribution series as
@@ -1229,7 +1229,7 @@
 
     >>> bug = new_firefox_bug()
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
 
     >>> submit_commands(bug, 'affects ubuntu/hoary')
@@ -1238,7 +1238,7 @@
 generic Ubuntu task, though.
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     Ubuntu
 
@@ -1246,7 +1246,7 @@
 target a bug directly, instead a nomination was created.
 
     >>> for nomination in bug.getNominations():
-    ...     print nomination.target.bugtargetdisplayname
+    ...     print(nomination.target.bugtargetdisplayname)
     Ubuntu Hoary
 
 The same happens if we try to target another series.
@@ -1254,12 +1254,12 @@
     >>> submit_commands(bug, 'affects ubuntu/warty')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     Ubuntu
 
     >>> for nomination in bug.getNominations():
-    ...     print nomination.target.bugtargetdisplayname
+    ...     print(nomination.target.bugtargetdisplayname)
     Ubuntu Hoary
     Ubuntu Warty
 
@@ -1268,12 +1268,12 @@
     >>> submit_commands(bug, 'affects ubuntu/warty')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     Ubuntu
 
     >>> for nomination in bug.getNominations():
-    ...     print nomination.target.bugtargetdisplayname
+    ...     print(nomination.target.bugtargetdisplayname)
     Ubuntu Hoary
     Ubuntu Warty
 
@@ -1297,7 +1297,7 @@
     >>> submit_commands(bug, 'affects ubuntu/grumpy')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     Ubuntu
     Ubuntu Grumpy
@@ -1307,7 +1307,7 @@
     >>> submit_commands(bug, 'affects ubuntu/warty')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     Ubuntu
     Ubuntu Warty
@@ -1318,7 +1318,7 @@
 
     >>> bug = new_firefox_bug()
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
 
     >>> submit_commands(bug, 'affects ubuntu/hoary/mozilla-firefox')
@@ -1327,7 +1327,7 @@
 task, and the series specific task.
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     mozilla-firefox (Ubuntu)
     mozilla-firefox (Ubuntu Hoary)
@@ -1344,18 +1344,18 @@
 
     >>> bug = new_firefox_bug()
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
 
     >>> submit_commands(bug, 'affects ubuntu/hoary/mozilla-firefox')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     mozilla-firefox (Ubuntu)
 
     >>> for nomination in bug.getNominations():
-    ...     print nomination.target.bugtargetdisplayname
+    ...     print(nomination.target.bugtargetdisplayname)
     Ubuntu Hoary
 
 Nominating product series work the same way as for distro series.
@@ -1364,20 +1364,20 @@
 
     >>> firefox = getUtility(IProductSet).getByName('firefox')
     >>> for driver in firefox.getSeries('trunk').drivers:
-    ...     print driver.displayname
+    ...     print(driver.displayname)
     Sample Person
 
     >>> login(sampledata.USER_EMAIL)
     >>> submit_commands(bug, 'affects /firefox/trunk')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Mozilla Firefox
     Mozilla Firefox trunk
     mozilla-firefox (Ubuntu)
 
     >>> for nomination in bug.getNominations():
-    ...     print nomination.target.bugtargetdisplayname
+    ...     print(nomination.target.bugtargetdisplayname)
     Mozilla Firefox trunk
     Ubuntu Hoary
 
@@ -1396,12 +1396,12 @@
     >>> submit_commands(bug, 'affects /evolution/trunk')
 
     >>> for bugtask in bug.bugtasks:
-    ...     print bugtask.bugtargetdisplayname
+    ...     print(bugtask.bugtargetdisplayname)
     Evolution
     Mozilla Firefox
 
     >>> for nomination in bug.getNominations():
-    ...     print nomination.target.bugtargetdisplayname
+    ...     print(nomination.target.bugtargetdisplayname)
     Evolution trunk
 
     >>> login(sampledata.USER_EMAIL)
@@ -1421,11 +1421,11 @@
     >>> len(bug_four.bugtasks)
     3
     >>> upstream_task = bug_four.bugtasks[0]
-    >>> print upstream_task.importance.name
+    >>> print(upstream_task.importance.name)
     CRITICAL
-    >>> print upstream_task.status.name
+    >>> print(upstream_task.status.name)
     CONFIRMED
-    >>> print upstream_task.assignee.displayname
+    >>> print(upstream_task.assignee.displayname)
     Sample Person
 
 
@@ -1443,11 +1443,11 @@
     >>> ignored = login_person(email_user)
 
     >>> submit_commands(bug_four, 'status wontfix')
-    >>> print upstream_task.status.title
+    >>> print(upstream_task.status.title)
     Won't Fix
 
     >>> submit_commands(bug_four, 'status expired')
-    >>> print upstream_task.status.title
+    >>> print(upstream_task.status.title)
     Expired
 
 Everyone else gets an explanatory error message:
@@ -1589,15 +1589,15 @@
 
     >>> def print_bugtask_modified_event(bugtask, event):
     ...     old_bugtask = event.object_before_modification
-    ...     print "event: bug %i %s => %s" % (bugtask.bug.id,
-    ...         old_bugtask.status.title, bugtask.status.title)
-    ...     print "event: bug %i %s => %s" % (bugtask.bug.id,
-    ...         old_bugtask.importance.title, bugtask.importance.title)
+    ...     print("event: bug %i %s => %s" % (bugtask.bug.id,
+    ...         old_bugtask.status.title, bugtask.status.title))
+    ...     print("event: bug %i %s => %s" % (bugtask.bug.id,
+    ...         old_bugtask.importance.title, bugtask.importance.title))
     >>> def print_bugtask_created_event(bugtask, event):
-    ...     print "event: new bugtask, bug %i %s" % (bugtask.bug.id,
-    ...         bugtask.status.title)
-    ...     print "event: new bugtask, bug %i %s" % (bugtask.bug.id,
-    ...         bugtask.importance.title)
+    ...     print("event: new bugtask, bug %i %s" % (bugtask.bug.id,
+    ...         bugtask.status.title))
+    ...     print("event: new bugtask, bug %i %s" % (bugtask.bug.id,
+    ...         bugtask.importance.title))
     >>> from lazr.lifecycle.interfaces import (
     ...     IObjectCreatedEvent, IObjectModifiedEvent)
     >>> from lp.testing.event import TestEventListener
@@ -1607,14 +1607,14 @@
     >>> bugtask_created_listener = TestEventListener(
     ...     IBugTask, IObjectCreatedEvent, print_bugtask_created_event)
     >>> bug_four_upstream_task = bug_four.bugtasks[0]
-    >>> print bug_four_upstream_task.status.name
+    >>> print(bug_four_upstream_task.status.name)
     NEW
-    >>> print bug_four_upstream_task.importance.name
+    >>> print(bug_four_upstream_task.importance.name)
     CRITICAL
     >>> bug_five_upstream_task = bug_five.bugtasks[0]
-    >>> print bug_five_upstream_task.status.name
+    >>> print(bug_five_upstream_task.status.name)
     NEW
-    >>> print bug_five_upstream_task.importance.name
+    >>> print(bug_five_upstream_task.importance.name)
     CRITICAL
     >>> submit_commands(bug_four,
     ...     'bug 4', 'status confirmed', 'importance medium',
@@ -1625,13 +1625,13 @@
     event: bug 4 Critical => Medium
     event: bug 5 New => Fix Released
     event: bug 5 Critical => High
-    >>> print bug_four_upstream_task.status.name
+    >>> print(bug_four_upstream_task.status.name)
     CONFIRMED
-    >>> print bug_four_upstream_task.importance.name
+    >>> print(bug_four_upstream_task.importance.name)
     MEDIUM
-    >>> print bug_five_upstream_task.status.name
+    >>> print(bug_five_upstream_task.status.name)
     FIXRELEASED
-    >>> print bug_five_upstream_task.importance.name
+    >>> print(bug_five_upstream_task.importance.name)
     HIGH
 
     >>> bugtask_modified_listener.unregister()
@@ -1655,13 +1655,13 @@
     1
     >>> submit_commands(bug_ten, 'status confirmed')
     >>> linux_task = bug_ten.bugtasks[0]
-    >>> print linux_task.status.name
+    >>> print(linux_task.status.name)
     CONFIRMED
 
     >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
-    >>> print bug_notification.bug.id
+    >>> print(bug_notification.bug.id)
     10
-    >>> print bug_notification.message.text_contents
+    >>> print(bug_notification.message.text_contents)
     ** Changed in: linux-source-2.6.15 (Ubuntu)
         Status: New => Confirmed
 
@@ -1677,9 +1677,9 @@
     >>> submit_commands(
     ...     bug_one, 'status confirmed', 'assignee test@xxxxxxxxxxxxx')
     >>> for bugtask in bug_one.bugtasks:
-    ...     print '%s: %s, assigned to %s' % (
+    ...     print('%s: %s, assigned to %s' % (
     ...         bugtask.bugtargetdisplayname, bugtask.status.title,
-    ...         getattr(bugtask.assignee, 'displayname', 'no one'))
+    ...         getattr(bugtask.assignee, 'displayname', 'no one')))
     Mozilla Firefox: Confirmed, assigned to Sample Person
     mozilla-firefox (Ubuntu): New, assigned to no one
     mozilla-firefox (Debian): Confirmed, assigned to no one
@@ -1687,8 +1687,8 @@
     >>> pending_notifications = BugNotification.select(
     ...     orderBy='-id', limit=2)
     >>> for bug_notification in pending_notifications:
-    ...     print bug_notification.bug.id
-    ...     print bug_notification.message.text_contents
+    ...     print(bug_notification.bug.id)
+    ...     print(bug_notification.message.text_contents)
     1
     ** Changed in: firefox
          Assignee: Mark Shuttleworth (mark) => Sample Person (name12)
@@ -1718,17 +1718,17 @@
     ...     bug_one, 'status confirmed',
     ...     'assignee kreutzm@xxxxxxxxxxxxxxxxxxx')
     >>> for bugtask in bug_one.bugtasks:
-    ...     print '%s: %s, assigned to %s' % (
+    ...     print('%s: %s, assigned to %s' % (
     ...         bugtask.bugtargetdisplayname, bugtask.status.title,
-    ...         getattr(bugtask.assignee, 'displayname', 'no one'))
+    ...         getattr(bugtask.assignee, 'displayname', 'no one')))
     Mozilla Firefox: Confirmed, assigned to Sample Person
     mozilla-firefox (Ubuntu): Confirmed, assigned to Helge Kreutzmann
     mozilla-firefox (Debian): Confirmed, assigned to no one
 
     >>> pending_notifications = BugNotification.select(orderBy='-id', limit=2)
     >>> for bug_notification in pending_notifications:
-    ...     print bug_notification.bug.id
-    ...     print bug_notification.message.text_contents
+    ...     print(bug_notification.bug.id)
+    ...     print(bug_notification.message.text_contents)
     1
     ** Changed in: mozilla-firefox (Ubuntu)
          Assignee: (unassigned) => Helge Kreutzmann (kreutzm)
@@ -1749,17 +1749,17 @@
     >>> submit_commands(
     ...     bug_one, 'status new', 'assignee test@xxxxxxxxxxxxx')
     >>> for bugtask in bug_one.bugtasks:
-    ...     print '%s: %s, assigned to %s' % (
+    ...     print('%s: %s, assigned to %s' % (
     ...         bugtask.bugtargetdisplayname, bugtask.status.title,
-    ...         getattr(bugtask.assignee, 'displayname', 'no one'))
+    ...         getattr(bugtask.assignee, 'displayname', 'no one')))
     Mozilla Firefox: Confirmed, assigned to Sample Person
     mozilla-firefox (Ubuntu): New, assigned to Sample Person
     mozilla-firefox (Debian): Confirmed, assigned to no one
 
     >>> pending_notifications = BugNotification.select(orderBy='-id', limit=2)
     >>> for bug_notification in pending_notifications:
-    ...     print bug_notification.bug.id
-    ...     print bug_notification.message.text_contents
+    ...     print(bug_notification.bug.id)
+    ...     print(bug_notification.message.text_contents)
     1
     ** Changed in: mozilla-firefox (Ubuntu)
          Assignee: Helge Kreutzmann (kreutzm) => Sample Person (name12)
@@ -1779,9 +1779,9 @@
     >>> submit_commands(
     ...     bug_one, 'status new', 'assignee foo.bar@xxxxxxxxxxxxx')
     >>> for bugtask in bug_one.bugtasks:
-    ...     print '%s: %s, assigned to %s' % (
+    ...     print('%s: %s, assigned to %s' % (
     ...         bugtask.bugtargetdisplayname, bugtask.status.title,
-    ...         getattr(bugtask.assignee, 'displayname', 'no one'))
+    ...         getattr(bugtask.assignee, 'displayname', 'no one')))
     Mozilla Firefox: Confirmed, assigned to Sample Person
     mozilla-firefox (Ubuntu): New, assigned to Sample Person
     mozilla-firefox (Debian): Confirmed, assigned to no one
@@ -2057,7 +2057,7 @@
 
 The first part is the error message, explaining what went wrong.
 
-    >>> print failure_msg.get_payload(decode=True)
+    >>> print(failure_msg.get_payload(decode=True))
     An error occurred while processing a mail you sent to Launchpad's email
     interface.
     <BLANKLINE>
@@ -2086,7 +2086,7 @@
     'Original Message Subject'
     >>> msg['Message-Id']
     '<original@msg>'
-    >>> print msg.get_payload(decode=True)
+    >>> print(msg.get_payload(decode=True))
     Original message body.
 
 Sometimes the original error was caused by the original message being
@@ -2136,7 +2136,7 @@
 exist without a contact address.
 
     >>> wartygnome = getUtility(IPersonSet).getByName('warty-gnome')
-    >>> print wartygnome.preferredemail
+    >>> print(wartygnome.preferredemail)
     None
 
 We send another email, creating a new task (for the package in ubuntu)
@@ -2148,7 +2148,7 @@
 The email was handled correctly - A new bugtask was added and assigned
 to the specified team.
 
-    >>> print ff_bug.bugtasks[-1].assignee.name
+    >>> print(ff_bug.bugtasks[-1].assignee.name)
     landscape-developers
 
 
@@ -2166,7 +2166,7 @@
     >>> firefox = getUtility(IProductSet).getByName('firefox')
     >>> for task in ff_bug.bugtasks:
     ...     if task.product == firefox:
-    ...         print task.status.name
+    ...         print(task.status.name)
     NEW
 
 Sample Person sends an email with several commands. First comes an
@@ -2191,7 +2191,7 @@
 
     >>> for task in ff_bug.bugtasks:
     ...     if task.product == firefox:
-    ...         print task.status.name
+    ...         print(task.status.name)
     CONFIRMED
 
 The 'subscribe' command failed, and the user is being notified of the
@@ -2200,7 +2200,7 @@
     >>> from_addr, to_addrs, raw_message = stub.test_emails[-1]
     >>> sent_msg = email.message_from_string(raw_message)
     >>> failure_msg, original_msg = sent_msg.get_payload()
-    >>> print failure_msg.get_payload(decode=True)
+    >>> print(failure_msg.get_payload(decode=True))
     An error occurred while processing a mail you sent to Launchpad's email
     interface.
     <BLANKLINE>
@@ -2239,7 +2239,7 @@
     >>> firefox = getUtility(IProductSet).getByName('firefox')
     >>> for task in ff_bug.bugtasks:
     ...     if task.product == firefox:
-    ...         print task.status.name
+    ...         print(task.status.name)
     CONFIRMED
 
 And the sender receives an email to let them know about the failing
@@ -2248,7 +2248,7 @@
     >>> from_addr, to_addrs, raw_message = stub.test_emails[-1]
     >>> sent_msg = email.message_from_string(raw_message)
     >>> failure_msg, original_msg = sent_msg.get_payload()
-    >>> print failure_msg.get_payload(decode=True)
+    >>> print(failure_msg.get_payload(decode=True))
     An error occurred while processing a mail you sent to Launchpad's email
     interface.
     <BLANKLINE>
@@ -2299,11 +2299,11 @@
 
     >>> for task in ff_bug.bugtasks:
     ...     if task.product == firefox:
-    ...         print task.importance.name
+    ...         print(task.importance.name)
     HIGH
     >>> for task in ff_bug.bugtasks:
     ...     if task.product == firefox:
-    ...         print task.status.name
+    ...         print(task.status.name)
     CONFIRMED
 
 
@@ -2322,7 +2322,7 @@
     ... """
     >>> process_email(submit_mail)
     >>> from_addr, to_addrs, raw_message = stub.test_emails[-1]
-    >>> print raw_message
+    >>> print(raw_message)
     Content-Type: text/plain; charset="utf-8"
     ...
     To: test@xxxxxxxxxxxxx
@@ -2356,7 +2356,7 @@
     ... Don't push the [#boom red button]!
     ... See you in {{{#launchpad}}}.
     ... """
-    >>> print reformat_wiki_text(wiki_text)
+    >>> print(reformat_wiki_text(wiki_text))
     = Sample Wiki Text =
     Some Text.
     Don't push the red button!
@@ -2371,14 +2371,15 @@
 
     >>> def print_attachments(attachments):
     ...     if len(list(attachments)) == 0:
-    ...         print "No attachments"
+    ...         print("No attachments")
     ...         return
     ...     transaction.commit()
     ...     for attachment in attachments:
     ...         lib = attachment.libraryfile
-    ...         print lib.__class__.__name__, lib.filename, lib.mimetype,
-    ...         print attachment.type.name
-    ...         print lib.read()
+    ...         print(lib.__class__.__name__, lib.filename, lib.mimetype,
+    ...               end=" ")
+    ...         print(attachment.type.name)
+    ...         print(lib.read())
     >>> login('test@xxxxxxxxxxxxx')
     >>> submit_mail = """From: Sample Person <test@xxxxxxxxxxxxx>
     ... To: new@xxxxxxxxxxxxxxxxxx
@@ -2451,8 +2452,8 @@
 
     >>> bug_notifications = BugNotification.select(orderBy='-id', limit=3)
     >>> for bug_notification in bug_notifications:
-    ...     print '-------------------'
-    ...     print bug_notification.message.chunks[0].content
+    ...     print('-------------------')
+    ...     print(bug_notification.message.chunks[0].content)
     -------------------
     Found a bug in Firefox. Nothing displayed. See attached files.
     <BLANKLINE>
@@ -2999,7 +3000,7 @@
     >>> comment_date = datetime(
     ...     2008, 5, 19, 16, 19, 12, tzinfo=pytz.timezone('Europe/Prague'))
 
-    >>> initial_mail = """From: test@xxxxxxxxxxxxx
+    >>> initial_mail = b"""From: test@xxxxxxxxxxxxx
     ... To: %(bug_id)s@malone-domain
     ... Date: %(date)s
     ... Message-Id: <76543@xxxxxxxxxxxxx>
@@ -3042,13 +3043,13 @@
     >>> transaction.commit()
 
     >>> [reply_message] = list(bug_with_watch.messages)[-1:]
-    >>> print reply_message.rfc822msgid
+    >>> print(reply_message.rfc822msgid)
     <1234567890@xxxxxxxxxxxxx>
 
 The parent of the new comment is set to the message which was imported
 from the remote bugtracker.
 
-    >>> print reply_message.parent.rfc822msgid
+    >>> print(reply_message.parent.rfc822msgid)
     <76543@xxxxxxxxxxxxx>
 
 The BugMessage instance which links the emailed comment to the bug also
@@ -3072,7 +3073,7 @@
     >>> comment_date = datetime(
     ...     2008, 5, 21, 11, 9, 12, tzinfo=pytz.timezone('Europe/Prague'))
 
-    >>> initial_mail = """From: test@xxxxxxxxxxxxx
+    >>> initial_mail = b"""From: test@xxxxxxxxxxxxx
     ... To: %(bug_id)s@malone-domain
     ... Date: %(date)s
     ... Message-Id: <912876543@xxxxxxxxxxxxx>
@@ -3111,5 +3112,5 @@
     ...     IBugMessageSet).getByBugAndMessage(
     ...         bug_with_watch, reply_message)
 
-    >>> print reply_bug_message.bugwatch
+    >>> print(reply_bug_message.bugwatch)
     None

=== modified file 'lib/lp/bugs/tests/bugtarget-questiontarget.txt'
--- lib/lp/bugs/tests/bugtarget-questiontarget.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/tests/bugtarget-questiontarget.txt	2018-06-30 11:02:51 +0000
@@ -131,7 +131,7 @@
     >>> from lp.bugs.model.bugnotification import BugNotification
     >>> bug_notifications = BugNotification.select(orderBy='-id')
     >>> for notification in bug_notifications:
-    ...     print notification.message.text_contents
+    ...     print(notification.message.text_contents)
     ** Converted to question:
        http://answers.launchpad.dev/.../+question/...
     ** Changed in: ...

=== modified file 'lib/lp/bugs/tests/bugzilla-api-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/bugzilla-api-xmlrpc-transport.txt	2018-06-23 00:51:17 +0000
+++ lib/lp/bugs/tests/bugzilla-api-xmlrpc-transport.txt	2018-06-30 11:02:51 +0000
@@ -43,7 +43,7 @@
 
 The Bugzilla_logincookie will now have been set for the transport, too.
 
-    >>> print bugzilla_transport.cookie_jar
+    >>> print(bugzilla_transport.cookie_jar)
     <RequestsCookieJar[<Cookie Bugzilla_login=...>,
                        <Cookie Bugzilla_logincookie=...>]>
 
@@ -65,7 +65,7 @@
 
     >>> time_dict = server.Bugzilla.time()
     >>> for key in sorted(time_dict):
-    ...     print "%s: %s" % (key, time_dict[key])
+    ...     print("%s: %s" % (key, time_dict[key]))
     db_time: 2008-05-01 01:01:01
     tz_name: UTC
     tz_offset: +0000
@@ -81,7 +81,7 @@
     >>> bugzilla_transport.timezone = 'CET'
     >>> time_dict = server.Bugzilla.time()
     >>> for key in sorted(time_dict):
-    ...     print "%s: %s" % (key, time_dict[key])
+    ...     print("%s: %s" % (key, time_dict[key]))
     db_time: 2008-05-01 01:01:01
     tz_name: CET
     tz_offset: +0100
@@ -101,7 +101,7 @@
     ...     {'ids': [1], 'permissive': True})
     >>> [bug_dict] = return_value['bugs']
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict[key])
+    ...     print("%s: %s" % (key, bug_dict[key]))
     alias:
     assigned_to: test@xxxxxxxxxxxxx
     component: GPPSystems
@@ -145,7 +145,7 @@
 
     >>> bug_dict = bug_dicts[0]
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict[key])
+    ...     print("%s: %s" % (key, bug_dict[key]))
     alias: bug-two
     assigned_to: marvin@xxxxxxxxxxxxxxxx
     component: Crew
@@ -174,7 +174,7 @@
     >>> bug_dicts = return_value['bugs']
     >>> for bug_dict in bug_dicts:
     ...     for key in sorted(bug_dict):
-    ...         print "%s: %s" % (key, bug_dict[key])
+    ...         print("%s: %s" % (key, bug_dict[key]))
     alias:
     assigned_to: test@xxxxxxxxxxxxx
     component: GPPSystems
@@ -213,7 +213,7 @@
     >>> bug_dicts = return_value['bugs']
     >>> for bug_dict in bug_dicts:
     ...     for key in sorted(bug_dict):
-    ...         print "%s: %s" % (key, bug_dict[key])
+    ...         print("%s: %s" % (key, bug_dict[key]))
     alias: bug-two
     assigned_to: marvin@xxxxxxxxxxxxxxxx
     component: Crew
@@ -238,7 +238,7 @@
     >>> bug_dicts = return_value['bugs']
     >>> for bug_dict in bug_dicts:
     ...     for key in sorted(bug_dict):
-    ...         print "%s: %s" % (key, bug_dict[key])
+    ...         print("%s: %s" % (key, bug_dict[key]))
     alias:
     assigned_to: test@xxxxxxxxxxxxx
     component: GPPSystems
@@ -296,17 +296,17 @@
     >>> import operator
     >>> def print_bug_comments(bugs_dict, sort_key='id'):
     ...     for key in sorted(bugs_dict):
-    ...         print "Bug %s:" % key
+    ...         print("Bug %s:" % key)
     ...         bug_comments = sorted(
     ...             bugs_dict[key]['comments'],
     ...             key=operator.itemgetter(sort_key))
     ...
     ...         for comment in bug_comments:
     ...             for comment_key in sorted(comment):
-    ...                 print "    %s: %s" % (
-    ...                     comment_key, comment[comment_key])
-    ...             print
-    ...         print
+    ...                 print("    %s: %s" % (
+    ...                     comment_key, comment[comment_key]))
+    ...             print()
+    ...         print()
 
 Passing a list of bug IDs to Bug.comments() will cause it to return all
 the comments for those bugs.
@@ -369,11 +369,11 @@
     >>> comments_dict = return_dict['comments']
 
     >>> for comment_id, comment in comments_dict.items():
-    ...     print "Comment %s:" % comment_id
+    ...     print("Comment %s:" % comment_id)
     ...     for comment_key in sorted(comment):
-    ...         print "    %s: %s" % (
-    ...             comment_key, comment[comment_key])
-    ...     print
+    ...         print("    %s: %s" % (
+    ...             comment_key, comment[comment_key]))
+    ...     print()
     Comment 1:
         author: trillian
         bug_id: 1
@@ -393,7 +393,7 @@
 Note that only comments have been returned when only 'comment_ids' have
 been passed. The bugs dict is empty.
 
-    >>> print return_dict['bugs']
+    >>> print(return_dict['bugs'])
     {}
 
 Passing an include_fields parameter allows us to limit which fields are
@@ -430,10 +430,10 @@
     >>> comments_dict = return_dict['comments']
 
     >>> for comment_id, comment in comments_dict.items():
-    ...     print "Comment %s:" % comment_id
+    ...     print("Comment %s:" % comment_id)
     ...     for comment_key in sorted(comment):
-    ...         print "    %s: %s" % (
-    ...             comment_key, comment[comment_key])
+    ...         print("    %s: %s" % (
+    ...             comment_key, comment[comment_key]))
     Comment 1:
         author: trillian
         id: 1
@@ -474,10 +474,10 @@
     >>> comments_dict = return_dict['comments']
 
     >>> for comment_id, comment in comments_dict.items():
-    ...     print "Comment %s:" % comment_id
+    ...     print("Comment %s:" % comment_id)
     ...     for comment_key in sorted(comment):
-    ...         print "    %s: %s" % (
-    ...             comment_key, comment[comment_key])
+    ...         print("    %s: %s" % (
+    ...             comment_key, comment[comment_key]))
     Comment 7:
         author: launchpad
         bug_id: 1
@@ -515,7 +515,7 @@
     ...     {'ids': [1], 'permissive': True})
     >>> bug_dict = return_value['bugs'][0]
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict.get(key))
+    ...     print("%s: %s" % (key, bug_dict.get(key)))
     alias:
     assigned_to: test@xxxxxxxxxxxxx
     component: GPPSystems

=== modified file 'lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt	2018-06-23 00:51:17 +0000
+++ lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt	2018-06-30 11:02:51 +0000
@@ -61,7 +61,7 @@
 
     >>> bugzilla_transport.setCookie(
     ...     "Bugzilla_logincookie=Want moar cookies plz")
-    >>> print server.Test.login_required()
+    >>> print(server.Test.login_required())
     Wonderful, you've logged in! Aren't you a clever biped?
 
 
@@ -85,12 +85,12 @@
 user id is always the same.
 
     >>> response_dict = server.Launchpad.login({'token': token_text})
-    >>> print response_dict['user_id']
+    >>> print(response_dict['user_id'])
     42
 
 The login cookies are in the transport's cookie jar.
 
-    >>> print bugzilla_transport.cookie_jar
+    >>> print(bugzilla_transport.cookie_jar)
     <RequestsCookieJar[<Cookie Bugzilla_login=...>,
                        <Cookie Bugzilla_logincookie=...>]>
 
@@ -104,7 +104,7 @@
 
     >>> time_dict = server.Launchpad.time()
     >>> for key in sorted(time_dict):
-    ...     print "%s: %s" % (key, time_dict[key])
+    ...     print("%s: %s" % (key, time_dict[key]))
     local_time: 2008-05-01 01:01:01
     tz_name: UTC
     utc_time: 2008-05-01 01:01:01
@@ -121,7 +121,7 @@
 
     >>> time_dict = server.Launchpad.time()
     >>> for key in sorted(time_dict):
-    ...     print "%s: %s" % (key, time_dict[key])
+    ...     print("%s: %s" % (key, time_dict[key]))
     local_time: 2008-05-15 16:19:53
     tz_name: US/Central
     utc_time: 2008-05-15 22:19:53
@@ -140,7 +140,7 @@
     ...     {'ids': [1], 'permissive': True})
     >>> [bug_dict] = return_value['bugs']
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict[key])
+    ...     print("%s: %s" % (key, bug_dict[key]))
     alias:
     assigned_to: test@xxxxxxxxxxxxx
     component: GPPSystems
@@ -165,8 +165,8 @@
     >>> bug_dicts = return_value['bugs']
     >>> for bug_dict in bug_dicts:
     ...     for key in sorted(bug_dict):
-    ...         print "%s: %s" % (key, bug_dict[key])
-    ...     print
+    ...         print("%s: %s" % (key, bug_dict[key]))
+    ...     print()
     alias:
     assigned_to: test@xxxxxxxxxxxxx
     component: GPPSystems
@@ -205,7 +205,7 @@
     ...     {'ids': ['bug-two'], 'permissive': True})
     >>> [bug_dict] = return_value['bugs']
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict[key])
+    ...     print("%s: %s" % (key, bug_dict[key]))
     alias: bug-two
     assigned_to: marvin@xxxxxxxxxxxxxxxx
     component: Crew
@@ -241,7 +241,7 @@
 
     >>> bug_dict = bug_dicts[0]
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict[key])
+    ...     print("%s: %s" % (key, bug_dict[key]))
     alias: bug-two
     assigned_to: marvin@xxxxxxxxxxxxxxxx
     component: Crew
@@ -287,7 +287,7 @@
 
     >>> bug_dict = bug_dicts[0]
     >>> for key in sorted(bug_dict):
-    ...     print "%s: %s" % (key, bug_dict[key])
+    ...     print("%s: %s" % (key, bug_dict[key]))
     alias: bug-two
     assigned_to: marvin@xxxxxxxxxxxxxxxx
     component: Crew
@@ -329,17 +329,17 @@
     >>> import operator
     >>> def print_bug_comments(bugs_dict, sort_key='number'):
     ...     for key in sorted(bugs_dict):
-    ...         print "Bug %s:" % key
+    ...         print("Bug %s:" % key)
     ...         bug_comments = sorted(
     ...             bugs_dict[key],
     ...             key=operator.itemgetter(sort_key))
     ...
     ...         for comment in bug_comments:
     ...             for comment_key in sorted(comment):
-    ...                 print "    %s: %s" % (
-    ...                     comment_key, comment[comment_key])
-    ...             print
-    ...         print
+    ...                 print("    %s: %s" % (
+    ...                     comment_key, comment[comment_key]))
+    ...             print()
+    ...         print()
 
 If Launchpad.comments() is passed a list of bug IDs it will return all
 the comments for all of those bugs.
@@ -424,7 +424,7 @@
 It's worth noting that, due to a quirk in the XML-RPC spec, the bug IDs
 in the returned 'bugs' dict are strings, not integers:
 
-    >>> print sorted(bugs_dict)
+    >>> print(sorted(bugs_dict))
     ['1', '2']
 
 
@@ -450,7 +450,7 @@
     >>> bugzilla_transport.auth_cookie = 'open sesame'
     >>> return_dict = server.Launchpad.add_comment(
     ...     {'id': 1, 'comment': comment})
-    >>> print return_dict['comment_id']
+    >>> print(return_dict['comment_id'])
     7
 
 The comment will be stored with the other comments on the remote server.
@@ -496,19 +496,19 @@
 
     >>> bugzilla_transport.auth_cookie = "here we go again"
     >>> result = server.Launchpad.set_link({'id': 1, 'launchpad_id': 10})
-    >>> print result['launchpad_id']
+    >>> print(result['launchpad_id'])
     0
 
 Otherwise, Launchpad.set_link() will return the ID of the last Launchpad
 bug linked to the remote bug.
 
     >>> result = server.Launchpad.set_link({'id': 1, 'launchpad_id': 11})
-    >>> print result['launchpad_id']
+    >>> print(result['launchpad_id'])
     10
 
 The new Launchpad bug ID will be recorded in the remote bug's
 `internals` dict.
 
     >>> for key, val in bugzilla_transport.bugs[1]['internals'].items():
-    ...     print "%s: %s" % (key, val)
+    ...     print("%s: %s" % (key, val))
     launchpad_id: 11

=== modified file 'lib/lp/bugs/tests/structural-subscription-target.txt'
--- lib/lp/bugs/tests/structural-subscription-target.txt	2011-12-24 15:18:32 +0000
+++ lib/lp/bugs/tests/structural-subscription-target.txt	2018-06-30 11:02:51 +0000
@@ -17,7 +17,7 @@
 
     >>> def print_subscriptions_list(subscriptions):
     ...     for subscription in subscriptions:
-    ...         print subscription.subscriber.name
+    ...         print(subscription.subscriber.name)
 
     >>> subscription = target.addBugSubscription(foobar, foobar)
     >>> print_subscriptions_list(target.getSubscriptions())
@@ -32,7 +32,7 @@
 
     >>> target.getSubscription(ubuntu_team)
     <...StructuralSubscription ...>
-    >>> print target.getSubscription(no_priv)
+    >>> print(target.getSubscription(no_priv))
     None
 
 To search for all subscriptions on a structure we use getSubscriptions.

=== modified file 'lib/lp/bugs/tests/test_doc.py'
--- lib/lp/bugs/tests/test_doc.py	2018-05-06 08:52:34 +0000
+++ lib/lp/bugs/tests/test_doc.py	2018-06-30 11:02:51 +0000
@@ -33,6 +33,7 @@
 from lp.testing.pages import PageTestSuite
 from lp.testing.systemdocs import (
     LayeredDocFileSuite,
+    setGlobs,
     setUp,
     tearDown,
     )
@@ -44,12 +45,12 @@
 def lobotomizeSteveASetUp(test):
     """Call lobotomize_stevea() and standard setUp"""
     lobotomize_stevea()
-    setUp(test)
+    setUp(test, future=True)
 
 
 def checkwatchesSetUp(test):
     """Setup the check watches script tests."""
-    setUp(test)
+    setUp(test, future=True)
     switch_dbuser(config.checkwatches.dbuser)
 
 
@@ -62,7 +63,7 @@
 def bugNotificationSendingSetUp(test):
     lobotomize_stevea()
     switch_dbuser(config.malone.bugnotification_dbuser)
-    setUp(test)
+    setUp(test, future=True)
 
 
 def bugNotificationSendingTearDown(test):
@@ -72,7 +73,7 @@
 def cveSetUp(test):
     lobotomize_stevea()
     switch_dbuser(config.cveupdater.dbuser)
-    setUp(test)
+    setUp(test, future=True)
 
 
 def uploaderBugsSetUp(test):
@@ -85,7 +86,7 @@
     lobotomize_stevea()
     test_dbuser = config.uploader.dbuser
     switch_dbuser(test_dbuser)
-    setUp(test)
+    setUp(test, future=True)
     test.globs['test_dbuser'] = test_dbuser
 
 
@@ -99,19 +100,19 @@
 
 def noPrivSetUp(test):
     """Set up a test logged in as no-priv."""
-    setUp(test)
+    setUp(test, future=True)
     login('no-priv@xxxxxxxxxxxxx')
 
 
 def bugtaskExpirationSetUp(test):
     """Setup globs for bug expiration."""
-    setUp(test)
+    setUp(test, future=True)
     login('test@xxxxxxxxxxxxx')
 
 
 def updateRemoteProductSetup(test):
     """Setup to use the 'updateremoteproduct' db user."""
-    setUp(test)
+    setUp(test, future=True)
     switch_dbuser(config.updateremoteproduct.dbuser)
 
 
@@ -122,17 +123,17 @@
 
 
 def bugSetStatusSetUp(test):
-    setUp(test)
+    setUp(test, future=True)
     test.globs['test_dbuser'] = config.processmail.dbuser
 
 
 def bugmessageSetUp(test):
-    setUp(test)
+    setUp(test, future=True)
     login('no-priv@xxxxxxxxxxxxx')
 
 
 def enableDSPPickerSetUp(test):
-    setUp(test)
+    setUp(test, future=True)
     ff = FeatureFixture({u'disclosure.dsp_picker.enabled': u'on'})
     ff.setUp()
     test.globs['dsp_picker_feature_fixture'] = ff
@@ -150,7 +151,7 @@
         ),
     'bug-heat.txt': LayeredDocFileSuite(
         '../doc/bug-heat.txt',
-        setUp=setUp,
+        setUp=lambda test: setUp(test, future=True),
         tearDown=tearDown,
         layer=LaunchpadZopelessLayer
         ),
@@ -198,7 +199,7 @@
         tearDown=bugNotificationSendingTearDown),
     'bug-export.txt': LayeredDocFileSuite(
         '../doc/bug-export.txt',
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadZopelessLayer
         ),
     'bug-set-status.txt': LayeredDocFileSuite(
@@ -224,7 +225,7 @@
     'bugtask-package-widget.txt': LayeredDocFileSuite(
         '../doc/bugtask-package-widget.txt',
         id_extensions=['bugtask-package-widget.txt'],
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadFunctionalLayer
         ),
     'bugtask-package-widget.txt-dsp-picker': LayeredDocFileSuite(
@@ -269,7 +270,7 @@
     'bugwatch.txt':
         LayeredDocFileSuite(
         '../doc/bugwatch.txt',
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadZopelessLayer
         ),
     'bug-watch-activity.txt':
@@ -281,7 +282,7 @@
     'bugtracker.txt':
         LayeredDocFileSuite(
         '../doc/bugtracker.txt',
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadZopelessLayer
         ),
     'checkwatches.txt':
@@ -302,7 +303,7 @@
     'externalbugtracker.txt':
         LayeredDocFileSuite(
         '../doc/externalbugtracker.txt',
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         stdout_logging_level=logging.WARNING,
         layer=LaunchpadZopelessLayer
         ),
@@ -441,7 +442,9 @@
         layer=LaunchpadZopelessLayer
         ),
     'filebug-data-parser.txt': LayeredDocFileSuite(
-        '../doc/filebug-data-parser.txt'),
+        '../doc/filebug-data-parser.txt',
+        setUp=lambda test: setGlobs(test, future=True),
+        ),
     'product-update-remote-product.txt': LayeredDocFileSuite(
         '../doc/product-update-remote-product.txt',
         setUp=updateRemoteProductSetup,
@@ -456,6 +459,7 @@
         ),
     'sourceforge-remote-products.txt': LayeredDocFileSuite(
         '../doc/sourceforge-remote-products.txt',
+        setUp=lambda test: setGlobs(test, future=True),
         layer=LaunchpadZopelessLayer,
         ),
     'bug-set-status.txt-processmail': LayeredDocFileSuite(
@@ -472,7 +476,7 @@
         stdout_logging=False),
     'bugs-emailinterface.txt-processmail': LayeredDocFileSuite(
         '../tests/bugs-emailinterface.txt',
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=ProcessMailLayer,
         stdout_logging=False),
     }
@@ -507,7 +511,8 @@
     for filename in filenames:
         path = os.path.join('../doc/', 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/bugs/tests/test_externalbugtracker.py'
--- lib/lp/bugs/tests/test_externalbugtracker.py	2011-12-28 17:03:06 +0000
+++ lib/lp/bugs/tests/test_externalbugtracker.py	2018-06-30 11:02:51 +0000
@@ -20,17 +20,20 @@
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(LayeredDocFileSuite(
-        'bugzilla-xmlrpc-transport.txt', setUp=setUp, tearDown=tearDown,
-        layer=LaunchpadFunctionalLayer))
-    suite.addTest(LayeredDocFileSuite(
-        'bugzilla-api-xmlrpc-transport.txt', setUp=setUp, tearDown=tearDown,
-        layer=LaunchpadFunctionalLayer))
-    suite.addTest(LayeredDocFileSuite(
-        'trac-xmlrpc-transport.txt', setUp=setUp, tearDown=tearDown,
+        'bugzilla-xmlrpc-transport.txt',
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
+        layer=LaunchpadFunctionalLayer))
+    suite.addTest(LayeredDocFileSuite(
+        'bugzilla-api-xmlrpc-transport.txt',
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
+        layer=LaunchpadFunctionalLayer))
+    suite.addTest(LayeredDocFileSuite(
+        'trac-xmlrpc-transport.txt',
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadFunctionalLayer))
     suite.addTest(LayeredDocFileSuite(
         'externalbugtracker-xmlrpc-transport.txt',
-        setUp=setUp, tearDown=tearDown,
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
         layer=LaunchpadFunctionalLayer))
 
     return suite

=== modified file 'lib/lp/bugs/tests/trac-xmlrpc-transport.txt'
--- lib/lp/bugs/tests/trac-xmlrpc-transport.txt	2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/tests/trac-xmlrpc-transport.txt	2018-06-30 11:02:51 +0000
@@ -42,8 +42,8 @@
 the current time is. It returns the local time zone, the local time, and
 the UTC time. The times are returned as seconds since epoch.
 
-    >>> server.launchpad.time_snapshot()
-    ['UTC', ..., ...]
+    >>> print(*server.launchpad.time_snapshot())
+    UTC ... ...
 
 It's possible to set which values will be returned, if the current time
 isn't suitable.
@@ -52,8 +52,8 @@
     >>> trac_transport.local_timezone = 'US/Eastern'
     >>> trac_transport.utc_offset = -4*60*60
 
-    >>> server.launchpad.time_snapshot()
-    ['US/Eastern', 1206328061, 1206342461]
+    >>> print(*server.launchpad.time_snapshot())
+    US/Eastern 1206328061 1206342461
 
 
 == launchpad.bug_info() ==
@@ -87,15 +87,15 @@
 bugs, along with a time snapshot as returned by time_snapshot().
 
     >>> time_snapshot, bugs = trac_transport.bug_info(level=0)
-    >>> print bugs
-    [{'id': '1'}, {'id': '2'}, {'id': '3'}]
+    >>> print(bugs)
+    [{'id': u'1'}, {'id': u'2'}, {'id': u'3'}]
 
 Specifying a level of 1 will return each bug's metadata, not including
 its last modified time.
 
     >>> def print_bugs(bug_list):
     ...     for bug in bug_list:
-    ...         print "%(id)s: %(status)s." % bug
+    ...         print("%(id)s: %(status)s." % bug)
 
     >>> time_snapshot, bugs = trac_transport.bug_info(level=1)
     >>> print_bugs(bugs)
@@ -124,24 +124,24 @@
 
     >>> time_snapshot, bugs = trac_transport.bug_info(level=2)
     >>> for bug in bugs:
-    ...     print "%s: %s" % (bug['id'], bug['comments'])
-    1: ['1-1']
-    2: ['2-1', '2-2']
+    ...     print("%s: %s" % (bug['id'], bug['comments']))
+    1: [u'1-1']
+    2: [u'2-1', u'2-2']
     3: []
 
 We'll also define a helper function to print comments out.
 
     >>> def print_bug_comment(comment):
     ...     for key in sorted(comment.keys()):
-    ...         print "%s: %s" % (key, comment[key])
-    ...     print ""
+    ...         print("%s: %s" % (key, comment[key]))
+    ...     print("")
 
 At level 3 the full list of comment dicts is returned along with the bug
 metadata, but not including comment authors.
 
     >>> time_snapshot, bugs = trac_transport.bug_info(level=3)
     >>> for bug in bugs:
-    ...     print "Comments for bug %s:" % bug['id']
+    ...     print("Comments for bug %s:" % bug['id'])
     ...     for comment in bug['comments']:
     ...         print_bug_comment(comment)
     Comments for bug 1:
@@ -177,8 +177,8 @@
     >>> time_snapshot, bugs = trac_transport.bug_info(
     ...     level=0, criteria=criteria)
 
-    >>> print bugs
-    [{'id': '1'}, {'id': '3'}]
+    >>> print(bugs)
+    [{'id': u'1'}, {'id': u'3'}]
 
 The bugs key in the criteria dict allows us to specify a list of bug IDs
 to return.
@@ -187,8 +187,8 @@
     >>> time_snapshot, bugs = trac_transport.bug_info(
     ...     level=0, criteria=criteria)
 
-    >>> print bugs
-    [{'id': '1'}, {'id': '2'}]
+    >>> print(bugs)
+    [{'id': u'1'}, {'id': u'2'}]
 
 If a bug doesn't exist, it will be returned with a status of
 'missing'.
@@ -197,8 +197,8 @@
     >>> time_snapshot, bugs = trac_transport.bug_info(
     ...     level=0, criteria=criteria)
 
-    >>> print bugs
-    [{'status': 'missing', 'id': '11'}, {'status': 'missing', 'id': '12'}]
+    >>> print(bugs)
+    [{'status': 'missing', 'id': u'11'}, {'status': 'missing', 'id': u'12'}]
 
 Combining the bugs and modified_since fields in the criteria dict will
 result in only the bugs modified since the modified_since time whose IDs
@@ -210,8 +210,8 @@
     >>> time_snapshot, bugs = trac_transport.bug_info(
     ...     level=0, criteria=criteria)
 
-    >>> print bugs
-    [{'id': '1'}]
+    >>> print(bugs)
+    [{'id': u'1'}]
 
 
 == launchpad.get_comments() ==
@@ -265,14 +265,14 @@
 
 add_comment() will return a new comment ID.
 
-    >>> print comment_id
+    >>> print(comment_id)
     3-1
 
 The comment will be included in the remote bug's comments.
 
     >>> for comment in trac_transport.remote_bugs['3'].comments:
     ...     for key in sorted(comment.keys()):
-    ...         print "%s: %s" % (key, comment[key])
+    ...         print("%s: %s" % (key, comment[key]))
     comment: This is a test comment being pushed.
     id: 3-1
     time: 1209399273
@@ -293,14 +293,14 @@
 the Launchpad bug for a given remote bug.
 
     >>> timestamp, launchpad_bug = trac_transport.get_launchpad_bug('1')
-    >>> print launchpad_bug
+    >>> print(launchpad_bug)
     42
 
 If the remote bug isn't currently linked to by a Launchpad bug,
 `launchpad.get_launchpad_bug()` will return 0 for the bug ID.
 
     >>> timestamp, launchpad_bug = trac_transport.get_launchpad_bug('2')
-    >>> print launchpad_bug
+    >>> print(launchpad_bug)
     0
 
 Calling `launchpad.get_launchpad_bug()` on a remote bug that doesn't
@@ -317,7 +317,7 @@
 
     >>> timestamp = trac_transport.set_launchpad_bug('2', 1)
     >>> timestamp, launchpad_bug = trac_transport.get_launchpad_bug('2')
-    >>> print launchpad_bug
+    >>> print(launchpad_bug)
     1
 
 Calling `launchpad.set_launchpad_bug()` will overwrite the existing
@@ -325,7 +325,7 @@
 
     >>> timestamp = trac_transport.set_launchpad_bug('2', 42)
     >>> timestamp, launchpad_bug = trac_transport.get_launchpad_bug('2')
-    >>> print launchpad_bug
+    >>> print(launchpad_bug)
     42
 
 Trying to call `launchpad.set_launchpad_bug()` on a remote bug that


Follow ups