Merge lp:~inspirated/launchpad/implement-Bug-findAttachments into lp:launchpad

Proposed by Kamran Riaz Khan
Status: Superseded
Proposed branch: lp:~inspirated/launchpad/implement-Bug-findAttachments
Merge into: lp:launchpad
Diff against target: 179 lines (+132/-0)
6 files modified
lib/lp/bugs/configure.zcml (+1/-0)
lib/lp/bugs/interfaces/bug.py (+8/-0)
lib/lp/bugs/model/bug.py (+18/-0)
lib/lp/bugs/tests/test_bug_find_attachment.py (+68/-0)
lib/lp/bugs/tests/testfiles/sample-attachment-alsa.diff (+15/-0)
lib/lp/bugs/tests/testfiles/sample-attachment-pidgin.diff (+22/-0)
To merge this branch: bzr merge lp:~inspirated/launchpad/implement-Bug-findAttachments
Reviewer Review Type Date Requested Status
Graham Binns (community) code Disapprove
Review via email: mp+27569@code.launchpad.net

This proposal has been superseded by a proposal from 2010-06-16.

Commit message

Implemented Bug.findAttachments()

Description of the change

Summary:
As part of my Google Summer of Code project, I had to implement attachment searching functionality in Arsenal. The end-product would allow user to specify a search string which would be searched in all the bug attachments for a project.

Doing this efficiently required two modifications in Launchpad:
    * Exposing a bug collection for a particular product
    * Implement a attachment search method for a particular bug (this branch)

Proposed fix:
Export a read operation findAttachments in IBug which returns a collection of IBugAttachment.

Pre-implementation Notes:
    * The search operation is essentially a text-only grep.

Implementation Details:
    * Zope configuration had to be updated to export the method
    * Multi-line searches break the GET request with a “HTTP/1.1 400 Bad Request” response
    * The attachments are all opened as regular file objects and then searched for the target pattern.

Tests:
bin/test -vv -m lp.bugs.tests.test_bug_find_attachment

Demo and Q/A:
Open any Launchpad bug in a browser:
 https://launchpad.dev/bugs/15
Create an attachment and upload any text file containing the string ‘char buf’.
Create a Launchpad instance:
 >>> from launchpadlib.launchpad import Launchpad
 >>> launchpad = Launchpad.login_with('just testing', 'https://api.launchpad.dev/beta/')
 The authorization page:
    (https://launchpad.dev/+authorize-token?oauth_token=ppd2bRZDnN6VX94lRqrq)
 should be opening in your browser. After you have authorized
 this program to access Launchpad on your behalf you should come
 back here and press <Enter> to finish the authentication process.
Load the bug:
 >>> bug = launchpad.bugs[15]
Search for the attachment containing the string ‘char buf’:
 >>> results = bug.findAttachments(text=u'char buf')
 >>> for attachment in results:
 ... print attachment.title
 ...
 Buffer Overflow Intro.txt

lint:
The changes are lint clean.

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

After a lengthy discussion in #launchpad-reviews we've agreed that there should be a different solution to this problem.

review: Disapprove (code)
11013. By Kamran Riaz Khan

Fixed unicode searches in Bug.findAttachments()

11014. By Kamran Riaz Khan

Changes in Bug.findAttachments():
 * Limited the size of attachments that can be searched to 8 MB
 * Modified searching to go-through attachments line-wise

11015. By Kamran Riaz Khan

Fixed Bug.findAttachments() to use preferred encoding instead of UTF-8

11016. By Kamran Riaz Khan

Added support for multi-line searches in Bug.findAttachments() using
is_encoded parameter

11017. By Kamran Riaz Khan

Changes in Bug.findAttachments():
 * Use Boyer-Moore-Horspool algorithm to search attachments in chunks
 * Fixed unicode and multiline unittests

11018. By Kamran Riaz Khan

Use encode('unicode_escape') instead of urllib.quote() for multiline
searches

11019. By Kamran Riaz Khan

Minor changes to improve code readability.

11020. By Kamran Riaz Khan

Removed urllib2 usage in Bug.findAttachments() by opening attachments
directly.

11021. By Kamran Riaz Khan

Minor changes to improve unittest readability.

Unmerged revisions

11021. By Kamran Riaz Khan

Minor changes to improve unittest readability.

11020. By Kamran Riaz Khan

Removed urllib2 usage in Bug.findAttachments() by opening attachments
directly.

11019. By Kamran Riaz Khan

Minor changes to improve code readability.

11018. By Kamran Riaz Khan

Use encode('unicode_escape') instead of urllib.quote() for multiline
searches

11017. By Kamran Riaz Khan

Changes in Bug.findAttachments():
 * Use Boyer-Moore-Horspool algorithm to search attachments in chunks
 * Fixed unicode and multiline unittests

11016. By Kamran Riaz Khan

Added support for multi-line searches in Bug.findAttachments() using
is_encoded parameter

11015. By Kamran Riaz Khan

Fixed Bug.findAttachments() to use preferred encoding instead of UTF-8

11014. By Kamran Riaz Khan

Changes in Bug.findAttachments():
 * Limited the size of attachments that can be searched to 8 MB
 * Modified searching to go-through attachments line-wise

11013. By Kamran Riaz Khan

Fixed unicode searches in Bug.findAttachments()

11012. By Kamran Riaz Khan

Added unit tests for Bug.findAttachments() method

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml 2010-06-04 09:31:21 +0000
+++ lib/lp/bugs/configure.zcml 2010-06-15 00:23:30 +0000
@@ -644,6 +644,7 @@
644 getBugWatch644 getBugWatch
645 canBeNominatedFor645 canBeNominatedFor
646 getNominationFor646 getNominationFor
647 findAttachments
647 getNominations648 getNominations
648 date_last_message649 date_last_message
649 number_of_duplicates650 number_of_duplicates
650651
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2010-06-10 18:55:22 +0000
+++ lib/lp/bugs/interfaces/bug.py 2010-06-15 00:23:30 +0000
@@ -361,6 +361,14 @@
361 latest_patch = Attribute("The most recent patch of this bug.")361 latest_patch = Attribute("The most recent patch of this bug.")
362362
363 @operation_parameters(363 @operation_parameters(
364 text=TextLine(title=_("Search text"), default=u""))
365 @operation_returns_collection_of(IBugAttachment)
366 @export_read_operation()
367 def findAttachments(text=""):
368 """Return all attachments related to this bug which match
369 <text>."""
370
371 @operation_parameters(
364 subject=optional_message_subject_field(),372 subject=optional_message_subject_field(),
365 content=copy_field(IMessage['content']))373 content=copy_field(IMessage['content']))
366 @call_with(owner=REQUEST_USER)374 @call_with(owner=REQUEST_USER)
367375
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-06-10 18:55:22 +0000
+++ lib/lp/bugs/model/bug.py 2010-06-15 00:23:30 +0000
@@ -812,6 +812,24 @@
812 notification.date_emailed = UTC_NOW812 notification.date_emailed = UTC_NOW
813 notification.syncUpdate()813 notification.syncUpdate()
814814
815 def findAttachments(self, text=u""):
816 """See `IBugAttachment`."""
817 if not isinstance(text, unicode):
818 raise TypeError, "`text` must be a unicode string"
819 if not text:
820 return EmptyResultSet()
821
822 store = IStore(Bug)
823
824 result = store.find(BugAttachment, BugAttachment.bug == self.id)
825 matches = []
826 for attachment in result:
827 data = attachment.data.read()
828 if text in data:
829 matches.append(attachment)
830
831 return matches
832
815 def newMessage(self, owner=None, subject=None,833 def newMessage(self, owner=None, subject=None,
816 content=None, parent=None, bugwatch=None,834 content=None, parent=None, bugwatch=None,
817 remote_comment_id=None):835 remote_comment_id=None):
818836
=== added file 'lib/lp/bugs/tests/test_bug_find_attachment.py'
--- lib/lp/bugs/tests/test_bug_find_attachment.py 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/tests/test_bug_find_attachment.py 2010-06-15 00:23:30 +0000
@@ -0,0 +1,68 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6import os
7import transaction
8import unittest
9
10from canonical.config import config
11from canonical.testing import LaunchpadZopelessLayer
12
13from lp.bugs.interfaces.bugjob import BugJobType
14from lp.bugs.model.bugjob import BugJob, BugJobDerived
15from lp.testing import TestCaseWithFactory
16
17from storm.store import EmptyResultSet
18
19from zope.security.proxy import isinstance
20
21class BugFindAttachmentTestCase(TestCaseWithFactory):
22 """Test case for bug attachment searches."""
23
24 layer = LaunchpadZopelessLayer
25
26 def setUp(self):
27 super(BugFindAttachmentTestCase, self).setUp()
28
29 dir = os.path.join(config.root, 'lib/lp/bugs/tests/testfiles')
30 filenames = [
31 'sample-attachment-alsa.diff',
32 'sample-attachment-pidgin.diff',
33 ]
34 self.searches = [
35 u'CONFIG_SND_HDA_POWER_SAVE',
36 u'PURPLE_CONNECTION_ERROR_NETWORK_ERROR',
37 ]
38 filepaths = [
39 os.path.join(dir, filename) for
40 filename in filenames
41 ]
42 person = self.factory.makePerson()
43 self.bug = self.factory.makeBug()
44
45 self.attachments = []
46 for filename, filepath in zip(filenames, filepaths):
47 attachment = self.factory.makeBugAttachment(
48 bug=self.bug, owner=person, data=file(filepath),
49 comment=filename, filename=filename)
50 self.attachments.append(attachment)
51
52 transaction.commit()
53
54 def test_text_type_check(self):
55 self.assertRaises(TypeError, self.bug.findAttachments, 42)
56
57 def test_empty_result_set(self):
58 result = self.bug.findAttachments(u'')
59 self.assertTrue(isinstance(result, EmptyResultSet))
60
61 def test_result_consistency(self):
62 for (search, attachment) in zip(self.searches, self.attachments):
63 result = self.bug.findAttachments(search)
64 self.assertEqual(1, len(result))
65 self.assertEqual(attachment, result[0])
66
67def test_suite():
68 return unittest.TestLoader().loadTestsFromName(__name__)
069
=== added file 'lib/lp/bugs/tests/testfiles/sample-attachment-alsa.diff'
--- lib/lp/bugs/tests/testfiles/sample-attachment-alsa.diff 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/tests/testfiles/sample-attachment-alsa.diff 2010-06-15 00:23:30 +0000
@@ -0,0 +1,15 @@
1Signed-off-by: Linuxant Support <support@linuxant.com>
2
3--- alsa-driver-1.0.16-lnxt/alsa-kernel/pci/hda/hda_codec.h 2008/04/04 19:28:32 1.1
4+++ alsa-driver-1.0.16-lnxt/alsa-kernel/pci/hda/hda_codec.h 2008/04/04 19:34:36
5@@ -464,6 +464,10 @@
6 unsigned int verb, unsigned int parm);
7 /* get a response from the last command */
8 unsigned int (*get_response)(struct hda_codec *codec);
9+ /* get the wall clock counter */
10+ u32 (*get_wallclock)(struct hda_codec *codec);
11+ /* get the link position counter */
12+ u32 (*get_linkpos)(struct snd_pcm_substream *substream);
13 /* free the private data */
14 void (*private_free)(struct hda_bus *);
15 #ifdef CONFIG_SND_HDA_POWER_SAVE
016
=== added file 'lib/lp/bugs/tests/testfiles/sample-attachment-pidgin.diff'
--- lib/lp/bugs/tests/testfiles/sample-attachment-pidgin.diff 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/tests/testfiles/sample-attachment-pidgin.diff 2010-06-15 00:23:30 +0000
@@ -0,0 +1,22 @@
1--- libpurple/protocols/jabber/jabber.c e98b3c84ef06f15c947b3190304799abef8f30b3
2+++ libpurple/protocols/jabber/jabber.c f005b3dede749de2f533f11c1c70a8575f2ef47a
3@@ -430,12 +430,17 @@ jabber_recv_cb_ssl(gpointer data, Purple
4 jabber_stream_init(js);
5 }
6
7- if(errno == EAGAIN)
8+ if(len < 0 && errno == EAGAIN)
9 return;
10- else
11+ else {
12+ if (len == 0)
13+ purple_debug_info("jabber", "Server closed the connection.\n");
14+ else
15+ purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno));
16 purple_connection_error_reason (js->gc,
17 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
18 _("Read Error"));
19+ }
20 }
21
22 static void