← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad/+git/security:suppress-orm-addresses into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad/+git/security:suppress-orm-addresses into launchpad:master.

Commit message:
Avoid showing memory addresses in API errors

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

The `__repr__` for a number of Launchpad objects included their memory addresses.  I've never found this useful for debugging, and, since these sometimes show up in error messages on the webservice API, they could potentially allow bypassing ASLR in attacks on the appserver.  Remove memory addresses from `__repr__` throughout.

Reported by halfdog.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad/+git/security:suppress-orm-addresses into launchpad:master.
diff --git a/lib/lp/answers/browser/tests/views.rst b/lib/lp/answers/browser/tests/views.rst
index 5f58b10..e2458ac 100644
--- a/lib/lp/answers/browser/tests/views.rst
+++ b/lib/lp/answers/browser/tests/views.rst
@@ -17,7 +17,7 @@ Several views are used to handle the various operations on a question.
 
     >>> login("test@xxxxxxxxxxxxx")
     >>> firefox_question.subscribe(firefox_question.owner)
-    <lp.answers.model.questionsubscription.QuestionSubscription...>
+    <QuestionSubscription...>
 
 
 QuestionSubscriptionView
diff --git a/lib/lp/answers/doc/expiration.rst b/lib/lp/answers/doc/expiration.rst
index 3ef2ab2..18b467e 100644
--- a/lib/lp/answers/doc/expiration.rst
+++ b/lib/lp/answers/doc/expiration.rst
@@ -75,14 +75,14 @@ somebody are subject to expiration.
     >>> recent_open_question.giveInfo(
     ...     "SVG works better now, but is still broken"
     ... )
-    <lp.answers.model.questionmessage.QuestionMessage...>
+    <QuestionMessage...>
 
     # This one was put in the NEEDSINFO state recently.
     >>> recent_needsinfo_question = questionset.get(4)
     >>> recent_needsinfo_question.requestInfo(
     ...     no_priv, "What URL were you visiting?"
     ... )
-    <lp.answers.model.questionmessage.QuestionMessage...>
+    <QuestionMessage...>
 
     # Old open questions.
     >>> old_open_question = questionset.get(5)
@@ -91,7 +91,7 @@ somebody are subject to expiration.
     # to make sure that DB permissions are correct.
     >>> admin_team = getUtility(IPersonSet).getByName("admins")
     >>> old_open_question.subscribe(admin_team)
-    <lp.answers.model.questionsubscription.QuestionSubscription...>
+    <QuestionSubscription...>
     >>> salgado = getUtility(IPersonSet).getByName("salgado")
     >>> old_open_question.target.addAnswerContact(salgado, salgado)
     True
diff --git a/lib/lp/answers/doc/notifications.rst b/lib/lp/answers/doc/notifications.rst
index de7700f..eab5b4a 100644
--- a/lib/lp/answers/doc/notifications.rst
+++ b/lib/lp/answers/doc/notifications.rst
@@ -326,7 +326,7 @@ giveInfo() transitions, let's see the other ones.
     # sent.
 
     >>> ubuntu_question.subscribe(sample_person)
-    <lp.answers.model.questionsubscription.QuestionSubscription ...>
+    <QuestionSubscription ...>
 
 
 Notifications for expireQuestion()
@@ -746,7 +746,7 @@ the notifications.
     >>> pt_BR_question.giveInfo(
     ...     "Veja o screenshot: http://tinyurl.com/y8jq8z";
     ... )
-    <lp.answers.model.questionmessage.QuestionMessage...>
+    <QuestionMessage...>
 
     >>> ignore = pop_questionemailjobs()
 
diff --git a/lib/lp/answers/doc/person.rst b/lib/lp/answers/doc/person.rst
index 3045fc4..1aa49cd 100644
--- a/lib/lp/answers/doc/person.rst
+++ b/lib/lp/answers/doc/person.rst
@@ -251,7 +251,7 @@ subscribed to...
     >>> pt_BR_question = getUtility(IQuestionSet).get(13)
     >>> login("foo.bar@xxxxxxxxxxxxx")
     >>> pt_BR_question.subscribe(foo_bar_raw)
-    <lp.answers.model.questionsubscription.QuestionSubscription...>
+    <QuestionSubscription...>
 
     >>> print(
     ...     ", ".join(
@@ -267,7 +267,7 @@ subscribed to...
 
     >>> es_question = getUtility(IQuestionSet).get(12)
     >>> es_question.reject(foo_bar_raw, "Reject question.")
-    <lp.answers.model.questionmessage.QuestionMessage...>
+    <QuestionMessage...>
 
     >>> print(
     ...     ", ".join(
@@ -297,7 +297,7 @@ subscribed to...
     >>> en_question = getUtility(IQuestionSet).get(1)
     >>> login("carlos@xxxxxxxxxxxxx")
     >>> en_question.addComment(carlos_raw, "A simple comment.")
-    <lp.answers.model.questionmessage.QuestionMessage...>
+    <QuestionMessage...>
 
     >>> print(
     ...     ", ".join(
diff --git a/lib/lp/answers/doc/question.rst b/lib/lp/answers/doc/question.rst
index 8f4e28c..825c8e3 100644
--- a/lib/lp/answers/doc/question.rst
+++ b/lib/lp/answers/doc/question.rst
@@ -89,7 +89,7 @@ Questions are manipulated through the IQuestion interface.
 The person who submitted the question is available in the owner field.
 
     >>> firefox_question.owner
-    <Person at ... name12 (Sample Person)>
+    <Person name12 (Sample Person)>
 
 When the question is created, the owner is added to the question's
 subscribers.
diff --git a/lib/lp/answers/doc/questionsets.rst b/lib/lp/answers/doc/questionsets.rst
index d2eb057..5431d84 100644
--- a/lib/lp/answers/doc/questionsets.rst
+++ b/lib/lp/answers/doc/questionsets.rst
@@ -304,7 +304,7 @@ It returns the number of open questions for each given package.
     >>> closed_question.setStatus(
     ...     closed_question.owner, QuestionStatus.SOLVED, "no comment"
     ... )
-    <lp.answers.model.questionmessage.QuestionMessage ...>
+    <QuestionMessage ...>
 
     >>> packages = (
     ...     ubuntu_evolution,
diff --git a/lib/lp/answers/doc/workflow.rst b/lib/lp/answers/doc/workflow.rst
index 770d60b..bf4d134 100644
--- a/lib/lp/answers/doc/workflow.rst
+++ b/lib/lp/answers/doc/workflow.rst
@@ -618,7 +618,7 @@ Users without launchpad.Moderator privileges cannot set the assignee.
     Traceback (most recent call last):
       ...
     zope.security.interfaces.Unauthorized:
-    (<lp.answers.model.question.Question ...>, 'assignee', 'launchpad.Append')
+    (<Question ...>, 'assignee', 'launchpad.Append')
 
 
 Events
diff --git a/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.rst b/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.rst
index 117249a..82b1666 100644
--- a/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.rst
+++ b/lib/lp/archiveuploader/tests/nascentupload-epoch-handling.rst
@@ -232,7 +232,7 @@ For source uploads, Changes.version == DSC.version == SPR.version:
     ...     component=bar_spr.component,
     ...     section=bar_spr.section,
     ... )
-    <SourcePackagePublishingHistory at ...>
+    <SourcePackagePublishingHistory object>
 
 Let's accept the source and claim 'build from accepted' to process the
 respective binary:
diff --git a/lib/lp/archiveuploader/tests/nascentupload.rst b/lib/lp/archiveuploader/tests/nascentupload.rst
index ba6d470..29cbca8 100644
--- a/lib/lp/archiveuploader/tests/nascentupload.rst
+++ b/lib/lp/archiveuploader/tests/nascentupload.rst
@@ -594,7 +594,7 @@ Then the source gets accepted and published, step 3 and 4:
     ...     component=multibar_spr.component,
     ...     section=multibar_spr.section,
     ... )
-    <SourcePackagePublishingHistory at ...>
+    <SourcePackagePublishingHistory object>
 
 Build creation is done based on the SourcePackageRelease object, step 5:
 
diff --git a/lib/lp/blueprints/doc/sprint-meeting-export.rst b/lib/lp/blueprints/doc/sprint-meeting-export.rst
index e251b98..94ca576 100644
--- a/lib/lp/blueprints/doc/sprint-meeting-export.rst
+++ b/lib/lp/blueprints/doc/sprint-meeting-export.rst
@@ -64,8 +64,7 @@ and check the list of interested people:
 
     >>> essential = False
     >>> ext_spec.subscribe(sampleperson, sampleperson, essential)
-    <lp.blueprints.model.specificationsubscription.SpecificationSubscription
-    object at ...>
+    <SpecificationSubscription object>
 
     >>> view = getMultiAdapter((ubz, request), name="+temp-meeting-export")
     >>> view.initialize()
diff --git a/lib/lp/blueprints/stories/sprints/xx-sprints.rst b/lib/lp/blueprints/stories/sprints/xx-sprints.rst
index 647972d..f64e4a6 100644
--- a/lib/lp/blueprints/stories/sprints/xx-sprints.rst
+++ b/lib/lp/blueprints/stories/sprints/xx-sprints.rst
@@ -484,10 +484,10 @@ First, we add a couple of IRC nicknames for Carlos.
     >>> login("carlos@xxxxxxxxxxxxx")
     >>> carlos = getUtility(IPersonSet).getByName("carlos")
     >>> IrcID(person=carlos, network="freenode", nickname="carlos")
-    <IrcID at ...>
+    <IrcID object>
 
     >>> IrcID(person=carlos, network="QuakeNet", nickname="qarlos")
-    <IrcID at ...>
+    <IrcID object>
 
     >>> for ircid in sorted(carlos.ircnicknames, key=attrgetter("nickname")):
     ...     print(ircid.nickname)
diff --git a/lib/lp/bugs/browser/tests/bug-views.rst b/lib/lp/bugs/browser/tests/bug-views.rst
index b1e522d..6b5dd09 100644
--- a/lib/lp/bugs/browser/tests/bug-views.rst
+++ b/lib/lp/bugs/browser/tests/bug-views.rst
@@ -65,7 +65,7 @@ comment, of the bug report.
     >>> launchbag.add(ubuntu)
 
     >>> ubuntu_filebug.initialize()
-    ObjectCreatedEvent: <Bug at ...>
+    ObjectCreatedEvent: <Bug object>
 
     >>> launchbag.clear()
 
@@ -103,7 +103,7 @@ the bugtask.
     >>> firefox_filebug = getMultiAdapter((firefox, request), name="+filebug")
 
     >>> firefox_filebug.initialize()
-    ObjectCreatedEvent: <Bug at ...>
+    ObjectCreatedEvent: <Bug object>
 
 3. Filing a bug on a distribution source package.
 
@@ -129,7 +129,7 @@ You can also access the +filebug page from a sourcepackage.
     >>> launchbag.add(ubuntu)
 
     >>> ubuntu_firefox_filebug.initialize()
-    ObjectCreatedEvent: <Bug at ...>
+    ObjectCreatedEvent: <Bug object>
 
     >>> launchbag.clear()
 
@@ -384,7 +384,7 @@ If the user isn't subscribed to the bug , 'Subscribe' is shown.
 If we subscribe Foo Bar, 'Edit subscription' is shown.
 
     >>> bug_one.subscribe(foo_bar, foo_bar)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
     >>> bug_menu = BugContextMenu(bug_one_bugtask)
     >>> bug_menu.subscription().text
     'Edit subscription'
@@ -399,7 +399,7 @@ still say 'Edit subscription':
     >>> foo_bar.inTeam(launchpad_team)
     True
     >>> bug_one.subscribe(launchpad_team, launchpad_team)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
     >>> bug_menu = BugContextMenu(bug_one_bugtask)
     >>> bug_menu.subscription().text
     'Edit subscription'
@@ -471,7 +471,7 @@ can't know if the user is subscribed or not.
 
     >>> ubuntu_team = getUtility(IPersonSet).getByName("ubuntu-team")
     >>> bug_two.subscribe(ubuntu_team, ubuntu_team)
-        <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
 
     >>> bug_two.markAsDuplicate(bug_three)
 
diff --git a/lib/lp/bugs/browser/tests/bugs-views.rst b/lib/lp/bugs/browser/tests/bugs-views.rst
index c0960bb..b6e1af8 100644
--- a/lib/lp/bugs/browser/tests/bugs-views.rst
+++ b/lib/lp/bugs/browser/tests/bugs-views.rst
@@ -111,7 +111,7 @@ If Person David gets subscribed to bug #4, he can see it in the list.
     ...     person_set.getByEmail("david@xxxxxxxxxxxxx"),
     ...     person_set.getByEmail("foo.bar@xxxxxxxxxxxxx"),
     ... )
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
     >>> login("david@xxxxxxxxxxxxx")
     >>> bugs_view = MaloneView(MaloneApplication(), LaunchpadTestRequest())
     >>> bugs_view.initialize()
diff --git a/lib/lp/bugs/browser/tests/bugtask-adding-views.rst b/lib/lp/bugs/browser/tests/bugtask-adding-views.rst
index 6ce5037..6830c37 100644
--- a/lib/lp/bugs/browser/tests/bugtask-adding-views.rst
+++ b/lib/lp/bugs/browser/tests/bugtask-adding-views.rst
@@ -361,7 +361,7 @@ linked to the new bug watch.
     >>> add_task_view = get_and_setup_view(
     ...     firefox_task, "+choose-affected-product", form
     ... )
-    ObjectCreatedEvent: <...BugWatch object at ...>
+    ObjectCreatedEvent: <...BugWatch object>
     ObjectCreatedEvent: <BugTask ...>
 
     >>> for bugtask in bug_four.bugtasks:
@@ -389,7 +389,7 @@ and the bug watch will be added without any confirmation needed:
     >>> add_task_view = get_and_setup_view(
     ...     firefox_task, "+choose-affected-product", form
     ... )
-    ObjectCreatedEvent: <...BugWatch object at ...>
+    ObjectCreatedEvent: <...BugWatch object>
     ObjectCreatedEvent: <BugTask ...>
 
     >>> print(add_task_view.notifications)
@@ -435,7 +435,7 @@ another bug links to the same bug.
     >>> add_task_view = get_and_setup_view(
     ...     bug_five_task, "+choose-affected-product", form
     ... )
-    ObjectCreatedEvent: <...BugWatch object at ...>
+    ObjectCreatedEvent: <...BugWatch object>
     ObjectCreatedEvent: <BugTask ...>
 
     >>> add_task_view.request.response.getHeader("Location")
diff --git a/lib/lp/bugs/browser/tests/person-bug-views.rst b/lib/lp/bugs/browser/tests/person-bug-views.rst
index 0e39122..376bc71 100644
--- a/lib/lp/bugs/browser/tests/person-bug-views.rst
+++ b/lib/lp/bugs/browser/tests/person-bug-views.rst
@@ -412,7 +412,7 @@ particular bug (see bug 1357):
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> bug_one = getUtility(IBugSet).get(1)
     >>> bug_one.newMessage(no_priv, "Some message", "Contents")
-    <Message at ...>
+    <Message id=...>
 
     >>> commented_bugtasks_view = create_view(no_priv, "+commentedbugs")
     >>> commented_bugs = list(commented_bugtasks_view.search().batch)
@@ -549,7 +549,7 @@ found.
 Once the user has commented, the related milestone does appear.
 
     >>> bug.newMessage(user)
-    <Message at ...>
+    <Message id=...>
 
     >>> print(pretty(commented_bugs_view.getMilestoneWidgetValues()))
     [{'checked': False,
@@ -574,7 +574,7 @@ are found.
 Once new_user has subscribed, the related milestones appear.
 
     >>> bug.subscribe(new_user, new_user)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
 
     >>> print(pretty(subscribed_bugs_view.getMilestoneWidgetValues()))
     [{'checked': False,
diff --git a/lib/lp/bugs/doc/bug-export.rst b/lib/lp/bugs/doc/bug-export.rst
index afe9b09..56063b1 100644
--- a/lib/lp/bugs/doc/bug-export.rst
+++ b/lib/lp/bugs/doc/bug-export.rst
@@ -149,7 +149,7 @@ the file when we later serialise the bug:
     ...     url=None,
     ...     description='"Hello World" attachment',
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment ...>
+    <BugAttachment ...>
     >>> bug4.addAttachment(
     ...     owner=sampleperson,
     ...     data=None,
@@ -158,7 +158,7 @@ the file when we later serialise the bug:
     ...     url="https://launchpad.net/";,
     ...     description=None,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment ...>
+    <BugAttachment ...>
 
     >>> transaction.commit()
 
diff --git a/lib/lp/bugs/doc/bug.rst b/lib/lp/bugs/doc/bug.rst
index 848028f..e1c54e5 100644
--- a/lib/lp/bugs/doc/bug.rst
+++ b/lib/lp/bugs/doc/bug.rst
@@ -1193,7 +1193,7 @@ We can also get the collection of users affected by a bug.
 
     >>> unaffecting_bug = factory.makeBug()
     >>> print(list(unaffecting_bug.users_affected))
-    [<Person at ...>]
+    [<Person ...>]
 
 Similarly, we can get the collection of users unaffected by a bug.
 
diff --git a/lib/lp/bugs/doc/bugattachments.rst b/lib/lp/bugs/doc/bugattachments.rst
index f33731b..1176420 100644
--- a/lib/lp/bugs/doc/bugattachments.rst
+++ b/lib/lp/bugs/doc/bugattachments.rst
@@ -59,7 +59,7 @@ ObjectCreatedEvent in order to trigger email notifications:
     ...     is_patch=False,
     ... )
     Attachment added: 'foo.bar'
-    <lp.bugs.model.bugattachment.BugAttachment ...>
+    <BugAttachment ...>
 
     >>> import transaction
     >>> transaction.commit()
@@ -613,7 +613,7 @@ LibraryFileAlias.restricted and Bug.private. See also the section
     ...     url=None,
     ...     comment="Some attachment",
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment ...>
+    <BugAttachment ...>
 
     >>> bug.attachments.count()
     1
@@ -646,7 +646,7 @@ meaningful description.
     ...     is_patch=True,
     ...     description="An attachment of some sort",
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment ...>
+    <BugAttachment ...>
 
     >>> bug.attachments.count()
     2
@@ -734,7 +734,7 @@ The method IBugAttachment.getFileByName() returns the Librarian file.
     >>> print(attachment.libraryfile.filename)
     foobar
     >>> attachment.getFileByName("foobar")
-    <LibraryFileAlias at...
+    <LibraryFileAlias object>
 
 A NotFoundError is raised if the file name passed to getFileByName()
 does not match the file name of the Librarian file.
diff --git a/lib/lp/bugs/doc/bugmessage-visibility.rst b/lib/lp/bugs/doc/bugmessage-visibility.rst
index 7f30db6..7522266 100644
--- a/lib/lp/bugs/doc/bugmessage-visibility.rst
+++ b/lib/lp/bugs/doc/bugmessage-visibility.rst
@@ -47,4 +47,4 @@ field.
     Traceback (most recent call last):
     ...
     zope.security.interfaces.Unauthorized:
-    (<Message at ...>, 'visible', 'launchpad.Admin')
+    (<Message id=...>, 'visible', 'launchpad.Admin')
diff --git a/lib/lp/bugs/doc/bugmessage.rst b/lib/lp/bugs/doc/bugmessage.rst
index 97b884a..3688d56 100644
--- a/lib/lp/bugs/doc/bugmessage.rst
+++ b/lib/lp/bugs/doc/bugmessage.rst
@@ -154,7 +154,7 @@ an external bug tracker.
     ... )
     >>> bugmsg = bug_one.linkMessage(message)
     >>> bugmsg
-    <BugMessage at...
+    <BugMessage ...
     >>> bugmsg.index
     4
     >>> for cve in bug_one.cves:
diff --git a/lib/lp/bugs/doc/bugsubscription.rst b/lib/lp/bugs/doc/bugsubscription.rst
index ac9fadd..3ccd0e4 100644
--- a/lib/lp/bugs/doc/bugsubscription.rst
+++ b/lib/lp/bugs/doc/bugsubscription.rst
@@ -86,7 +86,7 @@ IBug.getDirectSubscribers().
     >>> mark = personset.getByName("mark")
 
     >>> linux_source_bug.subscribe(mark, mark)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
 
     >>> print_displayname(linux_source_bug.getDirectSubscribers())
     Foo Bar
@@ -475,7 +475,7 @@ be unsubscribed from dupes.
     False
 
     >>> bug_six.subscribe(sample_person, sample_person)
-    <lp.bugs.model.bugsubscription.BugSubscription...>
+    <BugSubscription...>
 
     >>> bug_five.isSubscribedToDupes(sample_person)
     True
@@ -763,7 +763,7 @@ package.
 So, if the Ubuntu team is added as a bug supervisor to evolution:
 
     >>> evolution.addBugSubscription(ubuntu_team, ubuntu_team)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
 The team will be implicitly subscribed to the previous bug we
 created:
@@ -821,7 +821,7 @@ subscriptions themselves, rather than the subscribers.
     ... )
     >>> ff_bug = firefox.createBug(params)
     >>> ff_bug.subscribe(lifeless, mark)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
     >>> subscriptions = [
     ...     "%s: %s"
     ...     % (
@@ -843,7 +843,7 @@ subscriptions themselves, rather than the subscribers.
     >>> dupe_ff_bug = firefox.createBug(params)
     >>> dupe_ff_bug.markAsDuplicate(ff_bug)
     >>> dupe_ff_bug.subscribe(foobar, lifeless)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
     >>> for subscription in ff_bug.getSubscriptionsFromDuplicates():
     ...     print(
     ...         "%s: %s"
diff --git a/lib/lp/bugs/doc/bugtask-expiration.rst b/lib/lp/bugs/doc/bugtask-expiration.rst
index 3c8fa87..8087a48 100644
--- a/lib/lp/bugs/doc/bugtask-expiration.rst
+++ b/lib/lp/bugs/doc/bugtask-expiration.rst
@@ -482,7 +482,7 @@ No Privileges Person can't see the bug either...
 ... unless they're subscribed to the bug.
 
     >>> private_bug.subscribe(no_priv, sample_person)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
     >>> reset_bug_modified_date(private_bug, 351)
     >>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
     ...     0, user=no_priv, target=ubuntu
diff --git a/lib/lp/bugs/doc/externalbugtracker-comment-imports.rst b/lib/lp/bugs/doc/externalbugtracker-comment-imports.rst
index eb59826..bd9e7b1 100644
--- a/lib/lp/bugs/doc/externalbugtracker-comment-imports.rst
+++ b/lib/lp/bugs/doc/externalbugtracker-comment-imports.rst
@@ -230,7 +230,7 @@ A BugTrackerPerson record will have been created to map the new Person
 to the name 'noemail' on our example bugtracker.
 
     >>> bug_watch.bugtracker.getLinkedPersonByName("noemail")
-    <lp.bugs.model.bugtrackerperson.BugTrackerPerson ...>
+    <BugTrackerPerson ...>
 
 If the remote person is invalid (i.e. a Launchpad Person can't be
 created for them) an error will be logged and the comment will not be
@@ -396,7 +396,7 @@ DebBugs database. The message is linked to the bug watch for which it
 was imported.
 
     >>> bug_watch.addComment(message_two.rfc822msgid, message_two)
-    <BugMessage at ...>
+    <BugMessage ...>
     >>> bug_watch.hasComment(message_two.rfc822msgid)
     True
 
diff --git a/lib/lp/bugs/doc/initial-bug-contacts.rst b/lib/lp/bugs/doc/initial-bug-contacts.rst
index c80abcd..674665f 100644
--- a/lib/lp/bugs/doc/initial-bug-contacts.rst
+++ b/lib/lp/bugs/doc/initial-bug-contacts.rst
@@ -39,7 +39,7 @@ Let's login then to add a subscription:
     >>> login("foo.bar@xxxxxxxxxxxxx")
 
     >>> debian_firefox.addBugSubscription(sample_person, sample_person)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> for pbc in debian_firefox.bug_subscriptions:
     ...     print(pbc.subscriber.name)
@@ -50,13 +50,13 @@ Trying to add a subscription to a package when that person or team is
 already subscribe to that package will return the existing subscription.
 
     >>> debian_firefox.addBugSubscription(sample_person, sample_person)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
 Let's add an ITeam as one of the subscribers:
 
     >>> ubuntu_team = personset.getByName("ubuntu-team")
     >>> debian_firefox.addBugSubscription(ubuntu_team, ubuntu_team)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> from operator import attrgetter
 
@@ -143,10 +143,10 @@ subscriber to the new package is already subscribed to the bug:
 
     >>> daf = personset.getByName("daf")
     >>> ubuntu_pmount.addBugSubscription(daf, daf)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> ubuntu_pmount.addBugSubscription(sample_person, sample_person)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> with notify_modified(
     ...     bug_one_in_ubuntu_firefox, ["id", "title", "sourcepackagename"]
@@ -273,7 +273,7 @@ contact address. Let's add such a team as a subscriber.
     True
 
     >>> ubuntu_pmount.addBugSubscription(ubuntu_gnome, ubuntu_gnome)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> with notify_modified(
     ...     bug_one_in_ubuntu_firefox, ["sourcepackagename"]
diff --git a/lib/lp/bugs/doc/malone-xmlrpc.rst b/lib/lp/bugs/doc/malone-xmlrpc.rst
index 703bf0e..1f65bc7 100644
--- a/lib/lp/bugs/doc/malone-xmlrpc.rst
+++ b/lib/lp/bugs/doc/malone-xmlrpc.rst
@@ -284,7 +284,7 @@ the ID of the new LoginToken.
     >>> token_string = bugtracker_token_api.newBugTrackerToken()
     >>> token = getUtility(ILoginTokenSet)[token_string]
     >>> token
-    <LoginToken at ...>
+    <LoginToken object>
 
 The LoginToken generated will be of the LoginTokenType BUGTRACKER.
 
@@ -301,7 +301,7 @@ These requests are all handled by the private xml-rpc server.
     >>> token_string = bugtracker_api.newBugTrackerToken()
     >>> token = getUtility(ILoginTokenSet)[token_string]
     >>> token
-    <LoginToken at ...>
+    <LoginToken object>
 
     >>> print(token.tokentype.title)
     Launchpad is authenticating itself with a remote bug tracker.
diff --git a/lib/lp/bugs/doc/official-bug-tags.rst b/lib/lp/bugs/doc/official-bug-tags.rst
index aa9e1f4..2eba6f6 100644
--- a/lib/lp/bugs/doc/official-bug-tags.rst
+++ b/lib/lp/bugs/doc/official-bug-tags.rst
@@ -15,14 +15,14 @@ Distributions and products can define official bug tags.
     >>> distro_tag.tag = "PCI"
     >>> distro_tag.target = ubuntu
     >>> store.add(distro_tag)
-    <lp.bugs.model.bugtarget.OfficialBugTag object at...
+    <OfficialBugTag object>
 
     >>> firefox = getUtility(IProductSet).getByName("firefox")
     >>> product_tag = OfficialBugTag()
     >>> product_tag.tag = "bar"
     >>> product_tag.target = firefox
     >>> store.add(product_tag)
-    <lp.bugs.model.bugtarget.OfficialBugTag object at...
+    <OfficialBugTag object>
 
 We can add the same bug tag for different products and distributions.
 
@@ -30,7 +30,7 @@ We can add the same bug tag for different products and distributions.
     >>> distro_tag2.tag = "foo"
     >>> distro_tag2.distribution = ubuntu
     >>> store.add(distro_tag2)
-    <lp.bugs.model.bugtarget.OfficialBugTag object at...
+    <OfficialBugTag object>
     >>> store.flush()
 
 But bug tags must be unique for each product and distribution.
@@ -39,7 +39,7 @@ But bug tags must be unique for each product and distribution.
     >>> distro_tag3.tag = "PCI"
     >>> distro_tag3.distribution = ubuntu
     >>> store.add(distro_tag3)
-    <lp.bugs.model.bugtarget.OfficialBugTag object at...
+    <OfficialBugTag object>
     >>> store.flush()
     Traceback (most recent call last):
     storm.database.UniqueViolation: ...
@@ -142,13 +142,13 @@ Ordinary users cannot add and remove official bug tags.
     Traceback (most recent call last):
     ...
     zope.security.interfaces.Unauthorized:
-    (<Product at ...>, 'addOfficialBugTag', 'launchpad.Edit')
+    (<Product object>, 'addOfficialBugTag', 'launchpad.Edit')
 
     >>> firefox.removeOfficialBugTag("foo")
     Traceback (most recent call last):
     ...
     zope.security.interfaces.Unauthorized:
-    (<Product at ...>, 'removeOfficialBugTag', 'launchpad.Edit')
+    (<Product object>, 'removeOfficialBugTag', 'launchpad.Edit')
 
 Official tags are accessible as a list property of official tag targets.
 
diff --git a/lib/lp/bugs/doc/structural-subscriptions.rst b/lib/lp/bugs/doc/structural-subscriptions.rst
index 5faa62c..29e34bc 100644
--- a/lib/lp/bugs/doc/structural-subscriptions.rst
+++ b/lib/lp/bugs/doc/structural-subscriptions.rst
@@ -24,7 +24,7 @@ distribution.
     ...     )
     ...
     >>> ff_sub.target
-    <Product at ...>
+    <Product object>
 
     >>> with person_logged_in(foobar):
     ...     ubuntu_sub = ubuntu.addBugSubscription(
diff --git a/lib/lp/bugs/model/bugmessage.py b/lib/lp/bugs/model/bugmessage.py
index 3db0e33..54c8eb9 100644
--- a/lib/lp/bugs/model/bugmessage.py
+++ b/lib/lp/bugs/model/bugmessage.py
@@ -75,11 +75,7 @@ class BugMessage(StormBase):
         self.bugwatch = bugwatch
 
     def __repr__(self):
-        return "<BugMessage at 0x%x message=%s index=%s>" % (
-            id(self),
-            self.message,
-            self.index,
-        )
+        return "<BugMessage message=%s index=%s>" % (self.message, self.index)
 
 
 @implementer(IBugMessageSet)
diff --git a/lib/lp/bugs/model/bugtracker.py b/lib/lp/bugs/model/bugtracker.py
index 6197847..6976de2 100644
--- a/lib/lp/bugs/model/bugtracker.py
+++ b/lib/lp/bugs/model/bugtracker.py
@@ -852,6 +852,9 @@ class BugTrackerSet:
     def __init__(self):
         self.title = "Bug trackers registered in Launchpad"
 
+    def __repr__(self):
+        return "<BugTrackerSet object>"
+
     def get(self, bugtracker_id, default=None):
         """See `IBugTrackerSet`."""
         bugtracker = IStore(BugTracker).get(BugTracker, bugtracker_id)
diff --git a/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.rst b/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.rst
index bed6dba..cdacdae 100644
--- a/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.rst
+++ b/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.rst
@@ -316,7 +316,7 @@ Patches also appear as badges in bug listings.
     ...     comment=message,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at ...>
+    <BugAttachment object>
     >>> transaction.commit()
     >>> logout()
     >>> browser.open("http://bugs.launchpad.test/firefox/+bugs";)
diff --git a/lib/lp/bugs/stories/patches-view/patches-view.rst b/lib/lp/bugs/stories/patches-view/patches-view.rst
index 786a75d..3d3ce93 100644
--- a/lib/lp/bugs/stories/patches-view/patches-view.rst
+++ b/lib/lp/bugs/stories/patches-view/patches-view.rst
@@ -69,7 +69,7 @@ After we add a non-patch attachment to that bug, the patches view
 still shows no patches.
 
     >>> with_anybody(factory.makeBugAttachment)(bug=bug_a, is_patch=False)
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> anon_browser.open(
     ...     "http://bugs.launchpad.test/patchy-product-1/+patches";
@@ -92,7 +92,7 @@ patches view.
     ...     bug=bug_a,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> anon_browser.open(
     ...     "http://bugs.launchpad.test/patchy-product-1/+patches";
@@ -139,7 +139,7 @@ attachments, and various statuses...
     ...     bug=bug_b,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch c",
@@ -149,10 +149,10 @@ attachments, and various statuses...
     ...     bug=bug_b,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(bug=bug_c, is_patch=False)
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch d",
@@ -162,7 +162,7 @@ attachments, and various statuses...
     ...     bug=bug_c,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch e",
@@ -172,7 +172,7 @@ attachments, and various statuses...
     ...     bug=bug_c,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch f",
@@ -182,7 +182,7 @@ attachments, and various statuses...
     ...     bug=bug_c,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch g",
@@ -192,7 +192,7 @@ attachments, and various statuses...
     ...     bug=bug_d,
     ...     is_patch=True,
     ... )
-    <lp.bugs.model.bugattachment.BugAttachment object at...
+    <BugAttachment object>
     >>> transaction.commit()
 
 ...the youngest patch on each bug is visible in the patch report
diff --git a/lib/lp/bugs/stories/webservice/xx-bug-target.rst b/lib/lp/bugs/stories/webservice/xx-bug-target.rst
index 8fb47c7..22420b6 100644
--- a/lib/lp/bugs/stories/webservice/xx-bug-target.rst
+++ b/lib/lp/bugs/stories/webservice/xx-bug-target.rst
@@ -43,7 +43,7 @@ Not everyone can modify it however:
     Content-Length: ...
     Content-Type: text/plain...
     <BLANKLINE>
-    (<Product at ...>, 'bug_reporting_guidelines', 'launchpad.BugSupervisor')
+    (<Product object>, 'bug_reporting_guidelines', 'launchpad.BugSupervisor')
 
 
 Official Bug Tags
diff --git a/lib/lp/bugs/stories/webservice/xx-bug.rst b/lib/lp/bugs/stories/webservice/xx-bug.rst
index cbd273f..0bc2373 100644
--- a/lib/lp/bugs/stories/webservice/xx-bug.rst
+++ b/lib/lp/bugs/stories/webservice/xx-bug.rst
@@ -1475,7 +1475,7 @@ a 404 error, but do not generate an OOPS.
     Content-Length: ...
     ...
     <BLANKLINE>
-    Object: <...BugTrackerSet object at ...>, name: 'mozilla.org'
+    Object: <BugTrackerSet object>, name: 'mozilla.org'
 
 Naturally, if we follow the Location: header then we'll get the
 renamed bug tracker.
@@ -1510,7 +1510,7 @@ Non-admins can't disable a bugtracker through the API.
     ... )
     HTTP/1.1 401 Unauthorized
     ...
-    (<...BugTracker object at ...>, 'active', 'launchpad.Admin')
+    (<...BugTracker object>, 'active', 'launchpad.Admin')
 
 Admins can, however.
 
diff --git a/lib/lp/bugs/tests/bugs-emailinterface.rst b/lib/lp/bugs/tests/bugs-emailinterface.rst
index b0d3575..4c72ef4 100644
--- a/lib/lp/bugs/tests/bugs-emailinterface.rst
+++ b/lib/lp/bugs/tests/bugs-emailinterface.rst
@@ -871,7 +871,7 @@ demonstrate, let's first make no_privs an indirect subscriber from bug
     Sample Person
 
     >>> bug_six.subscribe(no_priv, no_priv)
-    <lp.bugs.model.bugsubscription.BugSubscription ...>
+    <BugSubscription ...>
 
     >>> for subscriber in sorted(
     ...     bug_five.getIndirectSubscribers(), key=attrgetter("displayname")
diff --git a/lib/lp/code/stories/webservice/xx-branchmergeproposal.rst b/lib/lp/code/stories/webservice/xx-branchmergeproposal.rst
index 94eb585..6a01464 100644
--- a/lib/lp/code/stories/webservice/xx-branchmergeproposal.rst
+++ b/lib/lp/code/stories/webservice/xx-branchmergeproposal.rst
@@ -564,7 +564,7 @@ which is the one we want the method to return.
     ...     source_branch=source_branch,
     ... )
     >>> proposal.nominateReviewer(target_owner, branch_owner)
-    <lp.code.model.codereviewvote.CodeReviewVoteReference object at ...>
+    <CodeReviewVoteReference object>
 
 And then we propose a merge the other way, so that the owner is target,
 but they have not been asked to review, meaning that the method shouldn't
@@ -578,7 +578,7 @@ return this review.
     ...     source_branch=target_branch,
     ... )
     >>> proposal.nominateReviewer(branch_owner, target_owner)
-    <lp.code.model.codereviewvote.CodeReviewVoteReference object at ...>
+    <CodeReviewVoteReference object>
     >>> logout()
 
     >>> proposals = webservice.named_get(
diff --git a/lib/lp/registry/browser/tests/people-views.rst b/lib/lp/registry/browser/tests/people-views.rst
index a94065a..0e4ea7a 100644
--- a/lib/lp/registry/browser/tests/people-views.rst
+++ b/lib/lp/registry/browser/tests/people-views.rst
@@ -65,19 +65,19 @@ one person and one team matching the 'test' string.
     >>> form = dict(name="test")
     >>> view = create_initialized_view(person_set, "+index", form=form)
     >>> print_batch(view.searchPeopleBatchNavigator())
-    <Person at ... name12 (Sample Person)>
-    <Person at ... testing-spanish-team (testing Spanish team)>
+    <Person name12 (Sample Person)>
+    <Person testing-spanish-team (testing Spanish team)>
 
 Searching for just people returns Sample Person.
 
     >>> form["searchfor"] = "peopleonly"
     >>> view = create_initialized_view(person_set, "+index", form=form)
     >>> print_batch(view.searchPeopleBatchNavigator())
-    <Person at ... name12 (Sample Person)>
+    <Person name12 (Sample Person)>
 
 Searching for just teams returns the testing Spanish team.
 
     >>> form["searchfor"] = "teamsonly"
     >>> view = create_initialized_view(person_set, "+index", form=form)
     >>> print_batch(view.searchPeopleBatchNavigator())
-    <Person at ... testing-spanish-team (testing Spanish team)>
+    <Person testing-spanish-team (testing Spanish team)>
diff --git a/lib/lp/registry/browser/tests/team-views.rst b/lib/lp/registry/browser/tests/team-views.rst
index af42f91..1958a37 100644
--- a/lib/lp/registry/browser/tests/team-views.rst
+++ b/lib/lp/registry/browser/tests/team-views.rst
@@ -112,14 +112,14 @@ page.  The display of mugshots is batched.
     >>> for person in list(batch):
     ...     print(removeSecurityProxy(person))
     ...
-    <Person at ... limi (Alexander Limi)>
-    <Person at ... cprov (Celso Providelo)>
-    <Person at ... kamion (Colin Watson)>
-    <Person at ... kinnison (Daniel Silverstone)>
-    <Person at ... edgar (Edgar Bursic)>
-    <Person at ... name16 (Foo Bar)>
-    <Person at ... jdub (Jeff Waugh)>
-    <Person at ... mark (Mark Shuttleworth)>
+    <Person limi (Alexander Limi)>
+    <Person cprov (Celso Providelo)>
+    <Person kamion (Colin Watson)>
+    <Person kinnison (Daniel Silverstone)>
+    <Person edgar (Edgar Bursic)>
+    <Person name16 (Foo Bar)>
+    <Person jdub (Jeff Waugh)>
+    <Person mark (Mark Shuttleworth)>
 
 
 Privacy and visibility
diff --git a/lib/lp/registry/doc/distribution-mirror.rst b/lib/lp/registry/doc/distribution-mirror.rst
index baadc6e..a3bb64d 100644
--- a/lib/lp/registry/doc/distribution-mirror.rst
+++ b/lib/lp/registry/doc/distribution-mirror.rst
@@ -1086,7 +1086,7 @@ for the status, however, they may not change it:
     Traceback (most recent call last):
     ...
     zope.security.interfaces.Unauthorized:
-    (<lp.registry.model.distributionmirror.DistributionMirror object at ...>,
+    (<DistributionMirror object>,
      'transitionToCountryMirror', 'launchpad.Admin')
 
 Mirror listing administrators may change the status however:
diff --git a/lib/lp/registry/doc/milestone.rst b/lib/lp/registry/doc/milestone.rst
index 7f8effb..1abc562 100644
--- a/lib/lp/registry/doc/milestone.rst
+++ b/lib/lp/registry/doc/milestone.rst
@@ -387,10 +387,10 @@ subscribed to milestone 1.0, and David is subscribed to milestone 2.0.
     >>> cprov = getUtility(IPersonSet).getByName("cprov")
     >>> ddaa = getUtility(IPersonSet).getByName("ddaa")
     >>> milestone_one.addBugSubscription(cprov, cprov)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> milestone_two.addBugSubscription(ddaa, ddaa)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
 We change the milestone for the task from 1.0 to 2.0, and fire the
 change event.
diff --git a/lib/lp/registry/doc/person-merge.rst b/lib/lp/registry/doc/person-merge.rst
index b1458b1..f50b89c 100644
--- a/lib/lp/registry/doc/person-merge.rst
+++ b/lib/lp/registry/doc/person-merge.rst
@@ -470,7 +470,7 @@ hand, are carried over just like when merging people.
     name12
 
     >>> list(IPollSubset(test_team).getAll())
-    [<lp.registry.model.poll.Poll object at ...]
+    [<Poll object>]
 
     # Landscape-developers has no super teams, two members and no polls.
 
diff --git a/lib/lp/registry/doc/person.rst b/lib/lp/registry/doc/person.rst
index 0e910b6..593fd24 100644
--- a/lib/lp/registry/doc/person.rst
+++ b/lib/lp/registry/doc/person.rst
@@ -1158,11 +1158,11 @@ alphabetically by package name.
     >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu")
     >>> pmount = ubuntu.getSourcePackage("pmount")
     >>> pmount.addBugSubscription(no_priv, no_priv)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> mozilla_firefox = ubuntu.getSourcePackage("mozilla-firefox")
     >>> mozilla_firefox.addBugSubscription(no_priv, no_priv)
-    <...StructuralSubscription object at ...>
+    <...StructuralSubscription object>
 
     >>> for package in no_priv.getBugSubscriberPackages():
     ...     print(package.name)
@@ -1546,7 +1546,7 @@ also takes the parameters hide_email_addresses, comment and registrant.
     ...     "testing _newPerson().",
     ...     foo_bar,
     ... )
-    <Person at ...>
+    <Person ...>
 
 If the name passed to _newPerson() is already taken, a NameAlreadyTaken
 error will be raised.
diff --git a/lib/lp/registry/doc/personroles.rst b/lib/lp/registry/doc/personroles.rst
index 90b301e..bd38b7b 100644
--- a/lib/lp/registry/doc/personroles.rst
+++ b/lib/lp/registry/doc/personroles.rst
@@ -26,7 +26,7 @@ PersonRoles is registered as an unnamed adapter for IPersonRoles.
     >>> from lp.registry.interfaces.role import IPersonRoles
     >>> person = factory.makePerson()
     >>> print(IPersonRoles(person))
-    <lp.registry.model.personroles.PersonRoles object at ...>
+    <PersonRoles ...>
 
 The original Person object can be reached through the person attribute.
 
diff --git a/lib/lp/registry/doc/private-team-roles.rst b/lib/lp/registry/doc/private-team-roles.rst
index 01f0364..6ad2bb0 100644
--- a/lib/lp/registry/doc/private-team-roles.rst
+++ b/lib/lp/registry/doc/private-team-roles.rst
@@ -176,7 +176,7 @@ Private teams can have structural subscriptions to products.
     ...     subscriber=priv_team, subscribed_by=team_owner
     ... )
     >>> sub.target
-    <Product at ...>
+    <Product object>
 
 
 Structural Subscription to Distributions
@@ -215,7 +215,7 @@ or private, can be the project registrant.
     Traceback (most recent call last):
     ...
     lp.registry.errors.PrivatePersonLinkageError: Cannot link person
-    (name=private-team, visibility=PRIVATE) to <Product at...
+    (name=private-team, visibility=PRIVATE) to <Product object> (name=...)
 
 
 Maintainer/Owner
diff --git a/lib/lp/registry/doc/productrelease-file-download.rst b/lib/lp/registry/doc/productrelease-file-download.rst
index 1a5ceaa..fca2b03 100644
--- a/lib/lp/registry/doc/productrelease-file-download.rst
+++ b/lib/lp/registry/doc/productrelease-file-download.rst
@@ -206,7 +206,7 @@ Only the product owner can create a new release.
     >>> milestone.createProductRelease(
     ...     firefox.owner, now, changelog="New in v2"
     ... )
-    <ProductRelease at ...>
+    <ProductRelease object>
     >>> for release in release_set.getReleasesForSeries(series):
     ...     print(release.version)
     ...
diff --git a/lib/lp/registry/doc/user-to-user.rst b/lib/lp/registry/doc/user-to-user.rst
index 238c4d5..5582852 100644
--- a/lib/lp/registry/doc/user-to-user.rst
+++ b/lib/lp/registry/doc/user-to-user.rst
@@ -165,9 +165,9 @@ to include his full name.  His contact is still recorded correctly.
     ...     .one()
     ... )
     >>> entry.sender
-    <Person at ... dave (Dave)>
+    <Person dave (Dave)>
     >>> entry.recipient
-    <Person at ... elly (Elly)>
+    <Person elly (Elly)>
 
 
 Adapters
diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
index 680cc87..bb3b265 100644
--- a/lib/lp/registry/model/person.py
+++ b/lib/lp/registry/model/person.py
@@ -503,7 +503,7 @@ class Person(
 
     def __repr__(self):
         displayname = backslashreplace(self.displayname)
-        return "<Person at 0x%x %s (%s)>" % (id(self), self.name, displayname)
+        return "<Person %s (%s)>" % (self.name, displayname)
 
     display_name = StringCol(dbName="displayname", notNull=True)
 
diff --git a/lib/lp/registry/model/personroles.py b/lib/lp/registry/model/personroles.py
index 0810a11..346847b 100644
--- a/lib/lp/registry/model/personroles.py
+++ b/lib/lp/registry/model/personroles.py
@@ -13,6 +13,7 @@ from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
 from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.role import IHasDrivers, IPersonRoles
+from lp.services.helpers import backslashreplace
 
 
 @adapter(IPerson)
@@ -25,6 +26,11 @@ class PersonRoles:
         self.inTeam = removeSecurityProxy(self.person).inTeam
         self.inAnyTeam = removeSecurityProxy(self.person).inAnyTeam
 
+    def __repr__(self):
+        # Compare Person.__repr__.
+        displayname = backslashreplace(self.person.displayname)
+        return "<PersonRoles %s (%s)>" % (self.person.name, displayname)
+
     def __getattr__(self, name):
         """Handle all in_* attributes."""
         prefix = "in_"
diff --git a/lib/lp/registry/stories/mailinglists/hosted-email-address.rst b/lib/lp/registry/stories/mailinglists/hosted-email-address.rst
index 2b0b0f6..349ca87 100644
--- a/lib/lp/registry/stories/mailinglists/hosted-email-address.rst
+++ b/lib/lp/registry/stories/mailinglists/hosted-email-address.rst
@@ -15,7 +15,7 @@ the address is reserved for the team which owns the ML.
     >>> from lp.services.identity.model.emailaddress import EmailAddressSet
     >>> email_set = EmailAddressSet()
     >>> email_set.getByEmail(mailing_list.address)
-    <EmailAddress at...
+    <EmailAddress ...
 
 The team owner sets the contact address to the hosted mailing list.
 
@@ -81,4 +81,4 @@ EmailAddress object for that team's mailing list will still be in the
 database.
 
     >>> email_set.getByEmail(mailing_list.address)
-    <EmailAddress at...
+    <EmailAddress ...
diff --git a/lib/lp/registry/stories/team-polls/create-poll-options.rst b/lib/lp/registry/stories/team-polls/create-poll-options.rst
index 5841009..6fc8edc 100644
--- a/lib/lp/registry/stories/team-polls/create-poll-options.rst
+++ b/lib/lp/registry/stories/team-polls/create-poll-options.rst
@@ -15,7 +15,7 @@ First we create a new poll to use throughout this test.
     ...     "dpl-2080",
     ...     "dpl-2080",
     ... )
-    <lp.registry.model.poll.Poll...
+    <Poll object>
     >>> logout()
 
 Our poll is not yet open, so new options can be added to it.
diff --git a/lib/lp/registry/stories/team-polls/edit-poll.rst b/lib/lp/registry/stories/team-polls/edit-poll.rst
index 2e1afc4..38a5a3e 100644
--- a/lib/lp/registry/stories/team-polls/edit-poll.rst
+++ b/lib/lp/registry/stories/team-polls/edit-poll.rst
@@ -15,7 +15,7 @@ First we create a new poll to use throughout this test.
     ...     "dpl-2080",
     ...     "dpl-2080",
     ... )
-    <lp.registry.model.poll.Poll...
+    <Poll object>
     >>> logout()
 
 Now we'll try to change its name to something that is already in use.
diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py
index c264f39..2b40f2c 100644
--- a/lib/lp/registry/tests/test_person.py
+++ b/lib/lp/registry/tests/test_person.py
@@ -1145,17 +1145,14 @@ class TestPersonStates(TestCaseWithFactory):
         person = self.factory.makePerson(
             name="user", displayname="\xdc-tester"
         )
-        ignore, name, displayname = repr(person).rsplit(" ", 2)
-        self.assertEqual("user", name)
-        self.assertEqual("(\\xdc-tester)>", displayname)
+        self.assertEqual("<Person user (\\xdc-tester)>", repr(person))
 
     def test_person_repr_unicode(self):
         # Verify that Unicode displayname is ascii safe.
         person = self.factory.makePerson(
             name="user", displayname="\u0170-tester"
         )
-        ignore, displayname = repr(person).rsplit(" ", 1)
-        self.assertEqual("(\\u0170-tester)>", displayname)
+        self.assertEqual("<Person user (\\u0170-tester)>", repr(person))
 
 
 class TestPersonRelatedBugTaskSearch(TestCaseWithFactory):
diff --git a/lib/lp/registry/tests/test_personroles.py b/lib/lp/registry/tests/test_personroles.py
index b2782ef..13ea66a 100644
--- a/lib/lp/registry/tests/test_personroles.py
+++ b/lib/lp/registry/tests/test_personroles.py
@@ -33,6 +33,20 @@ class TestPersonRoles(TestCaseWithFactory):
         roles = IPersonRoles(self.person)
         self.assertIs(self.person, roles.person)
 
+    def test_repr_ascii(self):
+        person = self.factory.makePerson(
+            name="user", displayname="\xdc-tester"
+        )
+        roles = IPersonRoles(person)
+        self.assertEqual("<PersonRoles user (\\xdc-tester)>", repr(roles))
+
+    def test_repr_unicode(self):
+        person = self.factory.makePerson(
+            name="user", displayname="\u0170-tester"
+        )
+        roles = IPersonRoles(person)
+        self.assertEqual("<PersonRoles user (\\u0170-tester)>", repr(roles))
+
     def _get_person_celebrities(self, is_team):
         for name in ILaunchpadCelebrities.names():
             attr = getattr(self.celebs, name)
diff --git a/lib/lp/services/database/sqlbase.py b/lib/lp/services/database/sqlbase.py
index 2a6aafe..c144f35 100644
--- a/lib/lp/services/database/sqlbase.py
+++ b/lib/lp/services/database/sqlbase.py
@@ -212,10 +212,7 @@ class SQLBase(storm.sqlobject.SQLObjectBase):
         return IStore(cls)
 
     def __repr__(self):
-        # XXX jamesh 2008-05-09:
-        # This matches the repr() output for the sqlos.SQLOS class.
-        # A number of the doctests rely on this formatting.
-        return "<%s at 0x%x>" % (self.__class__.__name__, id(self))
+        return "<%s object>" % (self.__class__.__name__)
 
     def destroySelf(self):
         my_primary = IPrimaryObject(self)
diff --git a/lib/lp/services/database/stormbase.py b/lib/lp/services/database/stormbase.py
index 1268501..65a1933 100644
--- a/lib/lp/services/database/stormbase.py
+++ b/lib/lp/services/database/stormbase.py
@@ -19,6 +19,9 @@ class StormBase(Storm):  # noqa: B1
     This class adds storm cache management functions to base.Storm.
     """
 
+    def __repr__(self):
+        return "<%s object>" % (self.__class__.__name__)
+
     def __eq__(self, other):
         """Equality operator.
 
diff --git a/lib/lp/services/identity/model/emailaddress.py b/lib/lp/services/identity/model/emailaddress.py
index e6557e5..d918b83 100644
--- a/lib/lp/services/identity/model/emailaddress.py
+++ b/lib/lp/services/identity/model/emailaddress.py
@@ -53,11 +53,7 @@ class EmailAddress(SQLBase, HasOwnerMixin):
     person = ForeignKey(dbName="person", foreignKey="Person", notNull=False)
 
     def __repr__(self):
-        return "<EmailAddress at 0x%x <%s> [%s]>" % (
-            id(self),
-            self.email,
-            self.status,
-        )
+        return "<EmailAddress <%s> [%s]>" % (self.email, self.status)
 
     def destroySelf(self):
         """See `IEmailAddress`."""
diff --git a/lib/lp/services/messages/model/message.py b/lib/lp/services/messages/model/message.py
index c6eb5b0..38c157f 100644
--- a/lib/lp/services/messages/model/message.py
+++ b/lib/lp/services/messages/model/message.py
@@ -133,7 +133,7 @@ class Message(SQLBase):
     visible = BoolCol(notNull=True, default=True)
 
     def __repr__(self):
-        return "<Message at 0x%x id=%s>" % (id(self), self.id)
+        return "<Message id=%s>" % self.id
 
     def __iter__(self):
         """See IMessage.__iter__"""
diff --git a/lib/lp/services/webservice/stories/xx-service.rst b/lib/lp/services/webservice/stories/xx-service.rst
index 8e655d5..46e8143 100644
--- a/lib/lp/services/webservice/stories/xx-service.rst
+++ b/lib/lp/services/webservice/stories/xx-service.rst
@@ -94,7 +94,7 @@ Anonymous requests can't change the dataset.
     >>> response.status
     401
     >>> print(six.ensure_text(response.body))
-    (<Person at...>, 'display_name', 'launchpad.Edit')
+    (<Person salgado (Guilherme Salgado)>, 'display_name', 'launchpad.Edit')
 
 A completely unsigned web service request is treated as an anonymous
 request, with the OAuth consumer name being equal to the User-Agent.
diff --git a/lib/lp/soyuz/browser/tests/archive-views.rst b/lib/lp/soyuz/browser/tests/archive-views.rst
index c8920af..19531ca 100644
--- a/lib/lp/soyuz/browser/tests/archive-views.rst
+++ b/lib/lp/soyuz/browser/tests/archive-views.rst
@@ -1243,7 +1243,7 @@ When 'No Privileges Person' gets upload right to Celso's PPA ...
     >>> login("foo.bar@xxxxxxxxxxxxx")
     >>> no_priv = getUtility(IPersonSet).getByName("no-priv")
     >>> cprov.archive.newComponentUploader(no_priv, "main")
-    <lp.soyuz.model.archivepermission.ArchivePermission ...>
+    <ArchivePermission ...>
 
 They become able to copy to the context PPA.
 
diff --git a/lib/lp/soyuz/doc/archive.rst b/lib/lp/soyuz/doc/archive.rst
index b564b12..3c14ec2 100644
--- a/lib/lp/soyuz/doc/archive.rst
+++ b/lib/lp/soyuz/doc/archive.rst
@@ -118,7 +118,7 @@ archives are published on disk.
     >>> cprov_archive.purpose = ArchivePurpose.COPY
     Traceback (most recent call last):
     ...
-    zope.security.interfaces.ForbiddenAttribute: ('purpose', <Archive at ...>)
+    zope.security.interfaces.ForbiddenAttribute: ('purpose', <Archive object>)
 
 'status' tracks the status of an Archive.  Its current values are only
 ACTIVE and DELETING.  ACTIVE is the normal value; DELETING is set when
@@ -1276,7 +1276,7 @@ it gets listed by `getPPAsForUser`.
     PPA for No Privileges Person
 
     >>> cprov_archive.newComponentUploader(no_priv, "main")
-    <lp.soyuz.model.archivepermission.ArchivePermission ...>
+    <ArchivePermission ...>
 
     >>> for ppa in archive_set.getPPAsForUser(no_priv):
     ...     print(ppa.displayname)
@@ -1290,7 +1290,7 @@ and user and give the team access to cprov's PPA:
     >>> uploader_team = factory.makeTeam(owner=cprov, name="uploader-team")
     >>> indirect_uploader = factory.makePerson(name="indirect-uploader")
     >>> cprov_archive.newComponentUploader(uploader_team, "main")
-    <lp.soyuz.model.archivepermission.ArchivePermission ...>
+    <ArchivePermission ...>
 
 'indirect_uploader' currently can't upload to cprov's PPA:
 
diff --git a/lib/lp/soyuz/doc/build-files.rst b/lib/lp/soyuz/doc/build-files.rst
index 554f85a..ddca43d 100644
--- a/lib/lp/soyuz/doc/build-files.rst
+++ b/lib/lp/soyuz/doc/build-files.rst
@@ -79,6 +79,6 @@ We can also retrieve the corresponding BinaryPackageFile:
 
     >>> bpf = build.getBinaryPackageFileByName("test_1.0_all.deb")
     >>> bpf
-    <...BinaryPackageFile object ...>
+    <BinaryPackageFile object>
     >>> bpf.libraryfile == deb
     True
diff --git a/lib/lp/soyuz/doc/distroarchseriesbinarypackagerelease.rst b/lib/lp/soyuz/doc/distroarchseriesbinarypackagerelease.rst
index 369032c..2138dde 100644
--- a/lib/lp/soyuz/doc/distroarchseriesbinarypackagerelease.rst
+++ b/lib/lp/soyuz/doc/distroarchseriesbinarypackagerelease.rst
@@ -38,10 +38,10 @@ Assemble our DARBPRs for fun and profit:
     ...         darbpr._latest_publishing_record(),
     ...     )
     ...
-    mozilla-firefox 0.9 <BinaryPackagePublishingHistory at 0x...>
+    mozilla-firefox 0.9 <BinaryPackagePublishingHistory object>
     mozilla-firefox 0.9 None
-    pmount 0.1-1 <BinaryPackagePublishingHistory at 0x...>
-    pmount 0.1-1 <BinaryPackagePublishingHistory at 0x...>
+    pmount 0.1-1 <BinaryPackagePublishingHistory object>
+    pmount 0.1-1 <BinaryPackagePublishingHistory object>
 
     >>> print(
     ...     mf_warty.status.title,
diff --git a/lib/lp/soyuz/doc/packageupload-lookups.rst b/lib/lp/soyuz/doc/packageupload-lookups.rst
index d7cfd1e..c673398 100644
--- a/lib/lp/soyuz/doc/packageupload-lookups.rst
+++ b/lib/lp/soyuz/doc/packageupload-lookups.rst
@@ -122,7 +122,7 @@ The `SourcePackageRelease` 'package_upload' and 'upload_changesfile'
 
     >>> original_source_upload = source.sourcepackagerelease.package_upload
     >>> print(original_source_upload)
-    <lp.soyuz.model.queue.PackageUpload ...>
+    <PackageUpload ...>
 
     >>> source_changesfile = source.sourcepackagerelease.upload_changesfile
     >>> original_source_upload.changesfile == source_changesfile
diff --git a/lib/lp/soyuz/doc/publishing.rst b/lib/lp/soyuz/doc/publishing.rst
index 9e39c8a..2c7ff8e 100644
--- a/lib/lp/soyuz/doc/publishing.rst
+++ b/lib/lp/soyuz/doc/publishing.rst
@@ -85,13 +85,13 @@ tiny 2-column content classes and force the users to retrieve those.
 Other properties are shortcuts to the source package's properties:
 
     >>> print(spph.package_creator)
-    <Person at ... mark (Mark Shuttleworth)>
+    <Person mark (Mark Shuttleworth)>
 
     >>> print(spph.package_maintainer)
-    <Person at ... mark (Mark Shuttleworth)>
+    <Person mark (Mark Shuttleworth)>
 
     >>> print(spph.package_signer)
-    <Person at ... name16 (Foo Bar)>
+    <Person name16 (Foo Bar)>
 
 The signer can also be None for packages that were synced (e.g. from Debian):
 
diff --git a/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst b/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
index 8eead97..6e8a79f 100644
--- a/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
+++ b/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
@@ -569,6 +569,6 @@ the error occurred based on the traceback included in the page.
     >>> print(extract_text(main_content))
     Lost something?
     ...NotFound:
-    Object: &lt;Archive at ...&gt;, name: 'test-pkg'...
+    Object: &lt;Archive object&gt;, name: 'test-pkg'...
     ...
 
diff --git a/lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.rst b/lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.rst
index b89f0c4..5c4d24f 100644
--- a/lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.rst
+++ b/lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.rst
@@ -744,10 +744,10 @@ result in a NotFound error.
     Traceback (most recent call last):
     ...
     zope.publisher.interfaces.NotFound:
-    Object: <Person at ... name16 (Foo Bar)>, name: '+archive'
+    Object: <Person name16 (Foo Bar)>, name: '+archive'
 
     >>> admin_browser.open("http://launchpad.test/~name16/+archive";)
     Traceback (most recent call last):
     ...
     zope.publisher.interfaces.NotFound:
-    Object: <Person at ... name16 (Foo Bar)>, name: '+archive'
+    Object: <Person name16 (Foo Bar)>, name: '+archive'
diff --git a/lib/lp/soyuz/stories/webservice/xx-archive.rst b/lib/lp/soyuz/stories/webservice/xx-archive.rst
index 351ae76..a2a2242 100644
--- a/lib/lp/soyuz/stories/webservice/xx-archive.rst
+++ b/lib/lp/soyuz/stories/webservice/xx-archive.rst
@@ -429,7 +429,7 @@ uploader or component uploader.
     >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'newPackageUploader', 'launchpad.Edit')
+    (<Archive object>, 'newPackageUploader', 'launchpad.Edit')
 
     >>> response = cjwatson_webservice.named_post(
     ...     ubuntu["main_archive_link"],
@@ -441,7 +441,7 @@ uploader or component uploader.
     >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'newPackagesetUploader', 'launchpad.Edit')
+    (<Archive object>, 'newPackagesetUploader', 'launchpad.Edit')
 
     >>> response = cjwatson_webservice.named_post(
     ...     ubuntu["main_archive_link"],
@@ -453,7 +453,7 @@ uploader or component uploader.
     >>> print(response)
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'newComponentUploader', 'launchpad.Edit')
+    (<Archive object>, 'newComponentUploader', 'launchpad.Edit')
 
 From here on we'll use ubuntu_owner, who does have permission as Ubuntu's
 owner.
@@ -1454,7 +1454,7 @@ Attempting to modify this flag without the necessary permissions will fail.
     >>> print(modify_archive(user_webservice, mark_archive))
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'authorized_size', 'launchpad.Moderate')
+    (<Archive object>, 'authorized_size', 'launchpad.Moderate')
 
 Private archives
 ~~~~~~~~~~~~~~~~
@@ -1667,7 +1667,7 @@ admins, commercial admins, PPA admins, and Launchpad developers.
     >>> print(modify_archive(user_webservice, pubpriv_archive))
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'private', 'launchpad.Moderate')
+    (<Archive object>, 'private', 'launchpad.Moderate')
 
     >>> login("foo.bar@xxxxxxxxxxxxx")
     >>> ppa_admin = factory.makePerson(
diff --git a/lib/lp/soyuz/stories/webservice/xx-archivedependency.rst b/lib/lp/soyuz/stories/webservice/xx-archivedependency.rst
index 46b5c5e..49989e8 100644
--- a/lib/lp/soyuz/stories/webservice/xx-archivedependency.rst
+++ b/lib/lp/soyuz/stories/webservice/xx-archivedependency.rst
@@ -57,7 +57,7 @@ can't get a list of the dependencies.
     >>> print(user_webservice.get("/~cprov/+archive/ubuntu/p3a/dependencies"))
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'dependencies', 'launchpad.SubscriberView')
+    (<Archive object>, 'dependencies', 'launchpad.SubscriberView')
 
 Nor can said user craft a URL to a dependency.
 
@@ -66,7 +66,7 @@ Nor can said user craft a URL to a dependency.
     ... )
     HTTP/1.1 401 Unauthorized
     ...
-    (<Archive at ...>, 'getArchiveDependency', 'launchpad.View')
+    (<Archive object>, 'getArchiveDependency', 'launchpad.View')
 
 Celso can see them if we grant private permissions, of course.
 
diff --git a/lib/lp/translations/doc/translationbranchapprover.rst b/lib/lp/translations/doc/translationbranchapprover.rst
index 7ce2405..4050aa7 100644
--- a/lib/lp/translations/doc/translationbranchapprover.rst
+++ b/lib/lp/translations/doc/translationbranchapprover.rst
@@ -85,7 +85,7 @@ It approves the entry which leads to the creation of a new POTemplate object.
     None
     >>> entry = approver.approve(entry)
     >>> print(repr(entry.potemplate))
-    <POTemplate at ...>
+    <POTemplate object>
     >>> foo_potemplate = entry.potemplate
     >>> print(foo_potemplate.name)
     foo
@@ -119,7 +119,7 @@ object.
     None
     >>> entry = approver.approve(entry)
     >>> print(repr(entry.potemplate))
-    <POTemplate at ...>
+    <POTemplate object>
     >>> bar_potemplate = entry.potemplate
     >>> print(bar_potemplate.name)
     bar

Follow ups