← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:stormify-bugattachment into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:stormify-bugattachment into launchpad:master.

Commit message:
Migrate BugAttachment to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/424282
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-bugattachment into launchpad:master.
diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py
index 14f37aa..7228fc7 100644
--- a/lib/lp/bugs/browser/bugtask.py
+++ b/lib/lp/bugs/browser/bugtask.py
@@ -242,7 +242,7 @@ def get_comments_for_bugtask(bugtask, truncate=False, for_display=False,
         get_property_cache(comment._message).bugattachments = []
 
     for attachment in bugtask.bug.attachments_unpopulated:
-        message_id = attachment._messageID
+        message_id = attachment._message_id
         if message_id not in comments:
             # We are not showing this message.
             continue
diff --git a/lib/lp/bugs/doc/bug-export.txt b/lib/lp/bugs/doc/bug-export.txt
index b0736e2..c78c506 100644
--- a/lib/lp/bugs/doc/bug-export.txt
+++ b/lib/lp/bugs/doc/bug-export.txt
@@ -140,7 +140,7 @@ the file when we later serialise the bug:
     >>> bug4.addAttachment(
     ...     sampleperson, io.BytesIO(b'Hello World'), 'Added attachment',
     ...     'hello.txt', description='"Hello World" attachment')
-    <BugAttachment ...>
+    <lp.bugs.model.bugattachment.BugAttachment ...>
 
     >>> transaction.commit()
 
diff --git a/lib/lp/bugs/doc/bugattachments.txt b/lib/lp/bugs/doc/bugattachments.txt
index 2b63a94..385e8f2 100644
--- a/lib/lp/bugs/doc/bugattachments.txt
+++ b/lib/lp/bugs/doc/bugattachments.txt
@@ -54,7 +54,7 @@ ObjectCreatedEvent in order to trigger email notifications:
     ...     comment=message,
     ...     is_patch=False)
     Attachment added: 'foo.bar'
-    <BugAttachment ...>
+    <lp.bugs.model.bugattachment.BugAttachment ...>
 
     >>> import transaction
     >>> transaction.commit()
@@ -549,7 +549,7 @@ LibraryFileAlias.restricted and Bug.private. See also the section
     >>> bug.linkAttachment(
     ...     owner=bug.owner, file_alias=file_alias,
     ...     comment="Some attachment")
-    <BugAttachment ...>
+    <lp.bugs.model.bugattachment.BugAttachment ...>
 
     >>> bug.attachments.count()
     1
@@ -575,7 +575,7 @@ meaningful description.
     ...     owner=bug.owner, file_alias=file_alias,
     ...     comment="Some attachment", is_patch=True,
     ...     description="An attachment of some sort")
-    <BugAttachment ...>
+    <lp.bugs.model.bugattachment.BugAttachment ...>
 
     >>> bug.attachments.count()
     2
diff --git a/lib/lp/bugs/interfaces/bugattachment.py b/lib/lp/bugs/interfaces/bugattachment.py
index 25ee2af..ef93552 100644
--- a/lib/lp/bugs/interfaces/bugattachment.py
+++ b/lib/lp/bugs/interfaces/bugattachment.py
@@ -81,13 +81,12 @@ class IBugAttachmentView(IHasBug):
               description=_(
                 'A short and descriptive description of the attachment'),
               required=True))
-    libraryfile = Bytes(title=_("The attachment content."),
-              required=True)
-    data = exported(
+    libraryfile = exported(
         Bytes(title=_("The attachment content."),
               required=True,
-              readonly=True))
-    _messageID = Int(title=_("Message ID"))
+              readonly=True),
+        exported_as="data")
+    _message_id = Int(title=_("Message ID"))
     message = exported(
         Reference(IMessage, title=_("The message that was created when we "
                                     "added this attachment.")))
@@ -100,7 +99,7 @@ class IBugAttachmentView(IHasBug):
         """Return the `ILibraryFileAlias` for the given file name.
 
         NotFoundError is raised if the given filename does not match
-        libraryfile.filename.
+        data.filename.
         """
 
 
@@ -128,14 +127,14 @@ class IBugAttachment(IBugAttachmentView, IBugAttachmentEdit):
             for attachment in bug.attachments:
                 buffer = attachment.data.open()
                 for line in buffer:
-                    print line
+                    print(line)
                 buffer.close()
 
         Launchpadlib example of accessing metadata about an attachment::
 
             attachment = bug.attachments[0]
-            print "title:", attachment.title
-            print "ispatch:", attachment.type
+            print("title:", attachment.title)
+            print("ispatch:", attachment.type)
 
         For information about the file-like object returned by
         attachment.data.open() see lazr.restfulclient's documentation of the
@@ -145,9 +144,9 @@ class IBugAttachment(IBugAttachmentView, IBugAttachmentEdit):
         the "message" attribute::
 
             message = attachment.message
-            print "subject:", message.subject.encode('utf-8')
-            print "owner:", message.owner.display_name.encode('utf-8')
-            print "created:", message.date_created
+            print("subject:", message.subject)
+            print("owner:", message.owner.display_name)
+            print("created:", message.date_created)
     """
 
 
diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
index 3f9bc62..4ae8c4e 100644
--- a/lib/lp/bugs/model/bug.py
+++ b/lib/lp/bugs/model/bug.py
@@ -2184,7 +2184,7 @@ class Bug(SQLBase, InformationTypeMixin):
         return store.find(
             (BugAttachment, LibraryFileAlias, LibraryFileContent),
             BugAttachment.bug == self,
-            BugAttachment.libraryfileID == LibraryFileAlias.id,
+            BugAttachment.libraryfile == LibraryFileAlias.id,
             LibraryFileContent.id == LibraryFileAlias.contentID,
             ).order_by(BugAttachment.id)
 
@@ -2207,7 +2207,7 @@ class Bug(SQLBase, InformationTypeMixin):
             attachment = row[0]
             # row[1] - the LibraryFileAlias is now in the storm cache and
             # will be found without a query when dereferenced.
-            indexed_message = message_to_indexed.get(attachment._messageID)
+            indexed_message = message_to_indexed.get(attachment._message_id)
             if indexed_message is not None:
                 get_property_cache(attachment).message = indexed_message
             return attachment
diff --git a/lib/lp/bugs/model/bugattachment.py b/lib/lp/bugs/model/bugattachment.py
index d200d3d..1aa211b 100644
--- a/lib/lp/bugs/model/bugattachment.py
+++ b/lib/lp/bugs/model/bugattachment.py
@@ -7,7 +7,12 @@ from lazr.lifecycle.event import (
     ObjectCreatedEvent,
     ObjectDeletedEvent,
     )
-from storm.store import Store
+from storm.locals import (
+    Int,
+    Reference,
+    Store,
+    Unicode,
+    )
 from zope.event import notify
 from zope.interface import implementer
 
@@ -18,33 +23,38 @@ from lp.bugs.interfaces.bugattachment import (
     IBugAttachmentSet,
     )
 from lp.services.database.enumcol import DBEnum
-from lp.services.database.sqlbase import SQLBase
-from lp.services.database.sqlobject import (
-    ForeignKey,
-    SQLObjectNotFound,
-    StringCol,
-    )
+from lp.services.database.interfaces import IStore
+from lp.services.database.stormbase import StormBase
 from lp.services.propertycache import cachedproperty
 
 
 @implementer(IBugAttachment)
-class BugAttachment(SQLBase):
+class BugAttachment(StormBase):
     """A bug attachment."""
 
-    _table = 'BugAttachment'
+    __storm_table__ = "BugAttachment"
 
-    bug = ForeignKey(
-        foreignKey='Bug', dbName='bug', notNull=True)
+    id = Int(primary=True)
+
+    bug_id = Int(name="bug", allow_none=False)
+    bug = Reference(bug_id, "Bug.id")
     type = DBEnum(
         enum=BugAttachmentType, allow_none=False,
         default=IBugAttachment['type'].default)
-    title = StringCol(notNull=True)
-    libraryfile = ForeignKey(
-        foreignKey='LibraryFileAlias', dbName='libraryfile', notNull=True)
-    data = ForeignKey(
-        foreignKey='LibraryFileAlias', dbName='libraryfile', notNull=True)
-    _message = ForeignKey(
-        foreignKey='Message', dbName='message', notNull=True)
+    title = Unicode(allow_none=False)
+    libraryfile_id = Int(name="libraryfile", allow_none=False)
+    libraryfile = Reference(libraryfile_id, "LibraryFileAlias.id")
+    _message_id = Int(name="message", allow_none=False)
+    _message = Reference(_message_id, "Message.id")
+
+    def __init__(self, bug, title, libraryfile, message,
+                 type=IBugAttachment["type"].default):
+        super().__init__()
+        self.bug = bug
+        self.title = title
+        self.libraryfile = libraryfile
+        self._message = message
+        self.type = type
 
     @cachedproperty
     def message(self):
@@ -72,7 +82,7 @@ class BugAttachment(SQLBase):
         # in order to avoid problems with not deleted files as described
         # in bug 387188.
         self.libraryfile.content = None
-        super().destroySelf()
+        Store.of(self).remove(self)
 
     def getFileByName(self, filename):
         """See IBugAttachment."""
@@ -91,9 +101,8 @@ class BugAttachmentSet:
             attach_id = int(attach_id)
         except ValueError:
             raise NotFoundError(attach_id)
-        try:
-            item = BugAttachment.get(attach_id)
-        except SQLObjectNotFound:
+        item = IStore(BugAttachment).get(BugAttachment, attach_id)
+        if item is None:
             raise NotFoundError(attach_id)
         return item
 
@@ -105,7 +114,7 @@ class BugAttachmentSet:
             attach_type = IBugAttachment['type'].default
         attachment = BugAttachment(
             bug=bug, libraryfile=filealias, type=attach_type, title=title,
-            _message=message)
+            message=message)
         # canonial_url(attachment) (called by notification subscribers
         # to generate the download URL of the attachments) blows up if
         # attachment.id is not (yet) set.
diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py
index db6b3f2..d0089e4 100644
--- a/lib/lp/bugs/model/bugtasksearch.py
+++ b/lib/lp/bugs/model/bugtasksearch.py
@@ -444,7 +444,7 @@ def _build_query(params):
             extra_clauses.append(
                 BugTaskFlat.bug_id.is_in(
                     Select(
-                        BugAttachment.bugID, tables=[BugAttachment],
+                        BugAttachment.bug_id, tables=[BugAttachment],
                         where=search_value_to_storm_where_condition(
                             BugAttachment.type, params.attachmenttype))))
 
diff --git a/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt b/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt
index 1e818e2..3ffdd6d 100644
--- a/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt
+++ b/lib/lp/bugs/stories/bugtask-searches/xx-listing-basics.txt
@@ -300,7 +300,7 @@ Patches also appear as badges in bug listings.
     ...     description="this fixes the bug",
     ...     comment=message,
     ...     is_patch=True)
-    <BugAttachment at ...>
+    <lp.bugs.model.bugattachment.BugAttachment object at ...>
     >>> transaction.commit()
     >>> logout()
     >>> browser.open('http://bugs.launchpad.test/firefox/+bugs')
diff --git a/lib/lp/bugs/stories/patches-view/patches-view.txt b/lib/lp/bugs/stories/patches-view/patches-view.txt
index e90e90d..934bf4c 100644
--- a/lib/lp/bugs/stories/patches-view/patches-view.txt
+++ b/lib/lp/bugs/stories/patches-view/patches-view.txt
@@ -59,7 +59,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)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> anon_browser.open(
     ...     'http://bugs.launchpad.test/patchy-product-1/+patches')
@@ -76,7 +76,7 @@ patches view.
     ...     comment="comment about patch a",
     ...     filename="patch_a.diff", owner=patch_submitter,
     ...     description="description of patch a", bug=bug_a, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> anon_browser.open(
     ...     'http://bugs.launchpad.test/patchy-product-1/+patches')
@@ -112,40 +112,40 @@ attachments, and various statuses...
     ...     comment="comment about patch b",
     ...     filename="patch_b.diff", owner=patch_submitter,
     ...     description="description of patch b", bug=bug_b, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch c",
     ...     filename="patch_c.diff", owner=patch_submitter,
     ...     description="description of patch c", bug=bug_b, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(bug=bug_c, is_patch=False)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch d",
     ...     filename="patch_d.diff", owner=patch_submitter,
     ...     description="description of patch d", bug=bug_c, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch e",
     ...     filename="patch_e.diff", owner=patch_submitter,
     ...     description="description of patch e", bug=bug_c, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch f",
     ...     filename="patch_f.diff", owner=patch_submitter,
     ...     description="description of patch f", bug=bug_c, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
     >>> with_anybody(factory.makeBugAttachment)(
     ...     comment="comment about patch g",
     ...     filename="patch_g.diff", owner=patch_submitter,
     ...     description="description of patch g", bug=bug_d, is_patch=True)
-    <BugAttachment at...
+    <lp.bugs.model.bugattachment.BugAttachment object at...
     >>> transaction.commit()
 
 ...the youngest patch on each bug is visible in the patch report
diff --git a/lib/lp/bugs/tests/test_bugchanges.py b/lib/lp/bugs/tests/test_bugchanges.py
index e89383f..0b017cc 100644
--- a/lib/lp/bugs/tests/test_bugchanges.py
+++ b/lib/lp/bugs/tests/test_bugchanges.py
@@ -871,6 +871,7 @@ class TestBugChanges(TestCaseWithFactory):
         # and BugNotification.
         attachment = self.factory.makeBugAttachment(
             bug=self.bug, owner=self.user)
+        attachment_title = attachment.title
         self.saveOldChanges()
         download_url = ProxiedLibraryFileAlias(
             attachment.libraryfile, attachment).http_url
@@ -887,13 +888,13 @@ class TestBugChanges(TestCaseWithFactory):
             'whatchanged': 'attachment removed',
             'newvalue': None,
             'oldvalue': '%s %s' % (
-                attachment.title, download_url),
+                attachment_title, download_url),
             }
 
         attachment_removed_notification = {
             'person': self.user,
             'text': '** Attachment removed: "%s"\n   %s' % (
-                attachment.title, download_url),
+                attachment_title, download_url),
             }
 
         self.assertRecordedChange(
diff --git a/lib/lp/services/messages/model/message.py b/lib/lp/services/messages/model/message.py
index 102a813..4b4b86b 100644
--- a/lib/lp/services/messages/model/message.py
+++ b/lib/lp/services/messages/model/message.py
@@ -35,6 +35,7 @@ from storm.locals import (
     Int,
     Max,
     Reference,
+    ReferenceSet,
     Store,
     Storm,
     Unicode,
@@ -126,7 +127,8 @@ class Message(SQLBase):
 
     raw = ForeignKey(foreignKey='LibraryFileAlias', dbName='raw',
                      default=None)
-    _bugattachments = SQLMultipleJoin('BugAttachment', joinColumn='_message')
+    _bugattachments = ReferenceSet(
+        "<primary key>", "BugAttachment._message_id")
 
     @cachedproperty
     def bugattachments(self):