← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/createBugParams-target into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/createBugParams-target into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/createBugParams-target/+merge/118673

This branch refactors target selection during bug creation to not be quite so terrible.

Previously CreateBugParams took an exploded IBugTarget -- the product, distribution and sourcepackagename were separated, so you couldn't just push an opaque object through. createBug and the mail handler ended up pretty ugly because of this. I've refactored them all to deal with opaque IBugTarget objects as much as possible, and the mail handler uses ISeriesBugTarget to detect whether it's dealing with a series task.

The only callsite that continues to deal in the ugly exploded target bits is the factory's makeBug. This branch is already large enough without fixing all of its callsites.

I also drove-by a decrufting of BugSet.createBug: it was still trying to set the removed legacy _private and _security_related DB columns.
-- 
https://code.launchpad.net/~wgrant/launchpad/createBugParams-target/+merge/118673
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/createBugParams-target into lp:launchpad.
=== modified file 'lib/lp/bugs/doc/bug.txt'
--- lib/lp/bugs/doc/bug.txt	2012-08-07 13:50:53 +0000
+++ lib/lp/bugs/doc/bug.txt	2012-08-08 05:50:32 +0000
@@ -93,11 +93,11 @@
     >>> from lp.registry.enums import InformationType
 
     >>> steve_harris = factory.makePerson(name='steve-harris')
+    >>> jukebox = factory.makeProduct()
     >>> params = CreateBugParams(
     ...     owner=steve_harris, title="Oh I hate this song",
-    ...     comment="Janick keeps playing Mr Blobby.")
-    >>> jukebox = factory.makeProduct()
-    >>> params.setBugTarget(product=jukebox)
+    ...     comment="Janick keeps playing Mr Blobby.",
+    ...     target=jukebox)
 
     >>> bug = bugset.createBug(params)
     New bug created: Oh I hate this song
@@ -113,8 +113,8 @@
     >>> params = CreateBugParams(
     ...     owner=steve_harris, filed_by=rod_smallwood,
     ...     title="Steve really hates this song",
-    ...     comment="He thinks Janick is doing it, but it's really me.")
-    >>> params.setBugTarget(product=jukebox)
+    ...     comment="He thinks Janick is doing it, but it's really me.",
+    ...     target=jukebox)
     >>> bug = bugset.createBug(params)
     New bug created: Steve really hates this song
               Owner: steve-harris
@@ -371,8 +371,8 @@
     >>> firefox = productset.get(4)
     >>> foobar = personset.getByEmail('foo.bar@xxxxxxxxxxxxx')
     >>> params = CreateBugParams(
-    ...     title="test firefox bug", comment="blah blah blah", owner=foobar)
-    >>> params.setBugTarget(product=firefox)
+    ...     title="test firefox bug", comment="blah blah blah", owner=foobar,
+    ...     target=firefox)
     >>> added_bug = getUtility(IBugSet).createBug(params)
     >>> public_bug = bugset.get(added_bug.id)
 
@@ -381,8 +381,7 @@
 
     >>> params = CreateBugParams(
     ...     title="test firefox bug", comment="blah blah blah",
-    ...     msg="foo foo foo", owner=foobar)
-    >>> params.setBugTarget(product=firefox)
+    ...     msg="foo foo foo", owner=foobar, target=firefox)
     >>> added_bug = getUtility(IBugSet).createBug(params)
     Traceback (most recent call last):
     ...
@@ -413,8 +412,7 @@
 
     >>> params = CreateBugParams(
     ...     title="test firefox bug", comment="blah blah blah", owner=foobar,
-    ...     information_type=InformationType.USERDATA)
-    >>> params.setBugTarget(product=firefox)
+    ...     information_type=InformationType.USERDATA, target=firefox)
     >>> added_bug = getUtility(IBugSet).createBug(params)
     >>> private_bug = bugset.get(added_bug.id)
 
@@ -438,8 +436,8 @@
     >>> evolution = spnset.get(9)
     >>> params = CreateBugParams(
     ...     title="test firefox bug", comment="blah blah blah",
-    ...     owner=foobar, information_type=InformationType.USERDATA)
-    >>> params.setBugTarget(distribution=ubuntu, sourcepackagename=evolution)
+    ...     owner=foobar, information_type=InformationType.USERDATA,
+    ...     target=ubuntu.getSourcePackage(evolution))
     >>> added_bug = getUtility(IBugSet).createBug(params)
     >>> private_bug = bugset.get(added_bug.id)
     >>> [subscriber.name for subscriber in private_bug.getDirectSubscribers()]

=== modified file 'lib/lp/bugs/errors.py'
--- lib/lp/bugs/errors.py	2012-06-06 05:58:44 +0000
+++ lib/lp/bugs/errors.py	2012-08-08 05:50:32 +0000
@@ -6,7 +6,6 @@
 __metaclass__ = type
 __all__ = [
     'BugCannotBePrivate',
-    'InvalidBugTargetType',
     'InvalidDuplicateValue',
 ]
 
@@ -17,11 +16,6 @@
 from lp.app.validators import LaunchpadValidationError
 
 
-@error_status(httplib.BAD_REQUEST)
-class InvalidBugTargetType(Exception):
-    """Bug target's type is not valid."""
-
-
 @error_status(httplib.EXPECTATION_FAILED)
 class InvalidDuplicateValue(LaunchpadValidationError):
     """A bug cannot be set as the duplicate of another."""

=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py	2012-08-06 03:47:42 +0000
+++ lib/lp/bugs/interfaces/bug.py	2012-08-08 05:50:32 +0000
@@ -102,8 +102,8 @@
     def __init__(self, owner, title, comment=None, description=None,
                  msg=None, status=None, datecreated=None,
                  information_type=None, subscribers=(), tags=None,
-                 subscribe_owner=True, filed_by=None, importance=None,
-                 milestone=None, assignee=None, cve=None):
+                 subscribe_owner=True, filed_by=None, target=None,
+                 importance=None, milestone=None, assignee=None, cve=None):
         self.owner = owner
         self.title = title
         self.comment = comment
@@ -113,9 +113,7 @@
         self.datecreated = datecreated
         self.information_type = information_type
         self.subscribers = subscribers
-        self.product = None
-        self.distribution = None
-        self.sourcepackagename = None
+        self.target = target
         self.tags = tags
         self.subscribe_owner = subscribe_owner
         self.filed_by = filed_by
@@ -124,41 +122,14 @@
         self.assignee = assignee
         self.cve = cve
 
-    def setBugTarget(self, product=None, distribution=None,
-                     sourcepackagename=None):
+    def setBugTarget(self, target):
         """Set the IBugTarget in which the bug is being reported.
 
-        :product: an IProduct
-        :distribution: an IDistribution
-        :sourcepackagename: an ISourcePackageName
-
-        A product or distribution must be provided, or an AssertionError
-        is raised.
-
-        If product is specified, all other parameters must evaluate to
-        False in a boolean context, or an AssertionError will be raised.
-
-        If distribution is specified, sourcepackagename may optionally
-        be provided. Product must evaluate to False in a boolean
-        context, or an AssertionError will be raised.
+        :target: an IBugTarget
         """
-        assert product or distribution, (
-            "You must specify the product or distribution in which this "
-            "bug exists")
-
-        if product:
-            conflicting_context = (
-                distribution or sourcepackagename)
-        elif distribution:
-            conflicting_context = product
-
-        assert not conflicting_context, (
-            "You must specify either an upstream context or a distribution "
-            "context, but not both.")
-
-        self.product = product
-        self.distribution = distribution
-        self.sourcepackagename = sourcepackagename
+        assert target, "You must speciy the target in which this bug exists"
+
+        self.target = target
 
 
 class BugNameField(ContentNameField):

=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
--- lib/lp/bugs/interfaces/bugtarget.py	2012-08-07 02:58:20 +0000
+++ lib/lp/bugs/interfaces/bugtarget.py	2012-08-08 05:50:32 +0000
@@ -427,5 +427,7 @@
 class ISeriesBugTarget(Interface):
     """An `IBugTarget` which is a series."""
 
+    series = Attribute(
+        "The product or distribution series of this series bug target.")
     bugtarget_parent = Attribute(
         "Non-series parent of this series bug target.")

=== modified file 'lib/lp/bugs/mail/commands.py'
--- lib/lp/bugs/mail/commands.py	2012-07-31 01:36:44 +0000
+++ lib/lp/bugs/mail/commands.py	2012-08-08 05:50:32 +0000
@@ -39,6 +39,7 @@
     IBugAddForm,
     IBugSet,
     )
+from lp.bugs.interfaces.bugtarget import ISeriesBugTarget
 from lp.bugs.interfaces.bugtask import (
     BugTaskImportance,
     BugTaskStatus,
@@ -54,13 +55,9 @@
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage,
     )
-from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.pillar import IPillarNameSet
 from lp.registry.interfaces.product import IProduct
-from lp.registry.interfaces.productseries import IProductSeries
 from lp.registry.interfaces.projectgroup import IProjectGroup
-from lp.registry.interfaces.sourcepackage import ISourcePackage
-from lp.registry.interfaces.sourcepackagename import ISourcePackageName
 from lp.services.mail.commands import (
     EditEmailCommand,
     EmailCommand,
@@ -603,12 +600,13 @@
 
         if isinstance(bug, CreateBugParams):
             # Enough information has been gathered to create a new bug.
-            kwargs = {
-                'product': IProduct(bug_target, None),
-                'distribution': IDistribution(bug_target, None),
-                'sourcepackagename': ISourcePackageName(bug_target, None),
-                }
-            bug.setBugTarget(**kwargs)
+            # If a series task is requested, create the non-series
+            # equivalent here. The series will be nominated/targeted in
+            # the remainder of the method.
+            if ISeriesBugTarget.providedBy(bug_target):
+                bug.target = bug_target.bugtarget_parent
+            else:
+                bug.target = bug_target
             bug, bug_event = getUtility(IBugSet).createBug(
                 bug, notify_event=False)
             event = ObjectCreatedEvent(bug.bugtasks[0])
@@ -643,28 +641,13 @@
 
         return bugtask, event, bug_event
 
-    def _targetBug(self, user, bug, series, sourcepackagename=None):
+    def _targetBug(self, user, bug, target):
         """Try to target the bug the given distroseries.
 
         If the user doesn't have permission to target the bug directly,
         only a nomination will be created.
         """
-        product = None
-        distribution = None
-        if IDistroSeries.providedBy(series):
-            distribution = series.distribution
-            if sourcepackagename:
-                general_target = distribution.getSourcePackage(
-                    sourcepackagename)
-            else:
-                general_target = distribution
-        else:
-            assert IProductSeries.providedBy(series), (
-                "Unknown series target: %r" % series)
-            assert sourcepackagename is None, (
-                "A product series can't have a source package.")
-            product = series.product
-            general_target = product
+        general_target = target.bugtarget_parent
         general_task = bug.getBugTask(general_target)
         if general_task is None:
             # A series task has to have a corresponding
@@ -674,22 +657,18 @@
         # We know the target is of the right type, and we just created
         # a pillar task, so if canBeNominatedFor == False then a task or
         # nomination must already exist.
-        if not bug.canBeNominatedFor(series):
+        if not bug.canBeNominatedFor(target.series):
             # A nomination has already been created.
-            nomination = bug.getNominationFor(series)
+            nomination = bug.getNominationFor(target.series)
         else:
-            nomination = bug.addNomination(target=series, owner=user)
+            nomination = bug.addNomination(target=target.series, owner=user)
 
         # Automatically approve an existing or new nomination if possible.
         if not nomination.isApproved() and nomination.canApprove(user):
             nomination.approve(user)
 
         if nomination.isApproved():
-            if sourcepackagename:
-                return bug.getBugTask(
-                    series.getSourcePackage(sourcepackagename))
-            else:
-                return bug.getBugTask(series)
+            return bug.getBugTask(target)
         else:
             # We can't return a nomination, so return the
             # distribution/product bugtask instead.
@@ -698,13 +677,8 @@
     def _create_bug_task(self, bug, bug_target):
         """Creates a new bug task with bug_target as the target."""
         user = getUtility(ILaunchBag).user
-        if (IProductSeries.providedBy(bug_target) or
-            IDistroSeries.providedBy(bug_target)):
+        if ISeriesBugTarget.providedBy(bug_target):
             return self._targetBug(user, bug, bug_target)
-        elif ISourcePackage.providedBy(bug_target):
-            return self._targetBug(
-                user, bug, bug_target.distroseries,
-                bug_target.sourcepackagename)
         else:
             return bug.addTask(user, bug_target)
 

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py	2012-08-07 13:50:53 +0000
+++ lib/lp/bugs/model/bug.py	2012-08-08 05:50:32 +0000
@@ -125,11 +125,13 @@
     NominationSeriesObsoleteError,
     )
 from lp.bugs.interfaces.bugnotification import IBugNotificationSet
+from lp.bugs.interfaces.bugtarget import ISeriesBugTarget
 from lp.bugs.interfaces.bugtask import (
     BugTaskStatus,
     BugTaskStatusSearch,
     IBugTask,
     IBugTaskSet,
+    IllegalTarget,
     UNRESOLVED_BUGTASK_STATUSES,
     )
 from lp.bugs.interfaces.bugtracker import BugTrackerType
@@ -231,10 +233,9 @@
     return Snapshot(
         bug_params, names=[
             "owner", "title", "comment", "description", "msg",
-            "datecreated", "information_type", "distribution",
-            "sourcepackagename", "product", "status", "subscribers", "tags",
-            "subscribe_owner", "filed_by", "importance", "milestone",
-            "assignee", "cve"])
+            "datecreated", "information_type", "target", "status",
+            "subscribers", "tags", "subscribe_owner", "filed_by",
+            "importance", "milestone", "assignee", "cve"])
 
 
 class BugTag(SQLBase):
@@ -2636,46 +2637,38 @@
         # of its attribute values below.
         params = snapshot_bug_params(bug_params)
 
-        context = params.product or params.distribution
+        if ISeriesBugTarget.providedBy(params.target):
+            raise IllegalTarget(
+                "Can't create a bug on a series. Create it with a non-series "
+                "task instead, and target it to the series afterwards.")
 
         if params.information_type is None:
-            if context is not None:
-                params.information_type = (
-                    context.getDefaultBugInformationType())
-            else:
-                params.information_type = InformationType.PUBLIC
+            params.information_type = (
+                params.target.pillar.getDefaultBugInformationType())
 
         bug, event = self._makeBug(params)
 
-        # Create the task on a product if one was passed.
-        if params.product:
-            getUtility(IBugTaskSet).createTask(
-                bug, params.owner, params.product, status=params.status)
-
-        # Create the task on a source package name if one was passed.
-        if params.distribution:
-            target = params.distribution
-            if params.sourcepackagename:
-                target = target.getSourcePackage(params.sourcepackagename)
-            getUtility(IBugTaskSet).createTask(
-                bug, params.owner, target, status=params.status)
+        # Create the initial task on the specified target.
+        getUtility(IBugTaskSet).createTask(
+            bug, params.owner, params.target, status=params.status)
 
         if params.information_type in SECURITY_INFORMATION_TYPES:
-            if context.security_contact:
-                bug.subscribe(context.security_contact, params.owner)
+            pillar = params.target.pillar
+            if pillar.security_contact:
+                bug.subscribe(pillar.security_contact, params.owner)
             else:
-                bug.subscribe(context.owner, params.owner)
+                bug.subscribe(pillar.owner, params.owner)
         # XXX: ElliotMurphy 2007-06-14: If we ever allow filing private
         # non-security bugs, this test might be simplified to checking
         # params.private.
-        elif params.product and params.product.private_bugs:
+        elif IProduct.providedBy(params.target) and params.target.private_bugs:
             # Subscribe the bug supervisor to all bugs,
             # because all their bugs are private by default
             # otherwise only subscribe the bug reporter by default.
-            if params.product.bug_supervisor:
-                bug.subscribe(params.product.bug_supervisor, params.owner)
+            if params.target.bug_supervisor:
+                bug.subscribe(params.target.bug_supervisor, params.owner)
             else:
-                bug.subscribe(params.product.owner, params.owner)
+                bug.subscribe(params.target.owner, params.owner)
 
         if params.subscribe_owner:
             bug.subscribe(params.owner, params.owner)
@@ -2744,15 +2737,10 @@
                 date_made_private=params.datecreated,
                 who_made_private=params.owner)
 
-        # Set the legacy attributes for now.
-        private = params.information_type in PRIVATE_INFORMATION_TYPES
-        security_related = (
-            params.information_type in SECURITY_INFORMATION_TYPES)
         bug = Bug(
             title=params.title, description=params.description,
             owner=params.owner, datecreated=params.datecreated,
             information_type=params.information_type,
-            _private=private, _security_related=security_related,
             **extra_params)
 
         if params.tags:

=== modified file 'lib/lp/bugs/model/bugtarget.py'
--- lib/lp/bugs/model/bugtarget.py	2012-08-07 02:58:20 +0000
+++ lib/lp/bugs/model/bugtarget.py	2012-08-08 05:50:32 +0000
@@ -19,8 +19,10 @@
     Storm,
     Unicode,
     )
+from zope.component import getUtility
 from zope.interface import implements
 
+from lp.bugs.interfaces.bug import IBugSet
 from lp.bugs.interfaces.bugtarget import IOfficialBugTag
 from lp.bugs.interfaces.bugtask import UNRESOLVED_BUGTASK_STATUSES
 from lp.bugs.interfaces.bugtasksearch import BugTagsSearchCombinator
@@ -121,6 +123,13 @@
             self.getBugSummaryContextWhereClause(),
             user, tag_limit=tag_limit, include_tags=include_tags)
 
+    def createBug(self, params):
+        """See IBugTarget."""
+        # createBug will raise IllegalTarget for ISeriesBugTargets and
+        # IProjectGroup.
+        params.target = self
+        return getUtility(IBugSet).createBug(params)
+
 
 class OfficialBugTagTargetMixin:
     """See `IOfficialBugTagTarget`.

=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
--- lib/lp/bugs/model/tests/test_bugtask.py	2012-08-07 02:31:56 +0000
+++ lib/lp/bugs/model/tests/test_bugtask.py	2012-08-08 05:50:32 +0000
@@ -3348,16 +3348,15 @@
         # accepted and have targetnamecache updated.
         ubuntu = getUtility(IDistributionSet).get(1)
 
+        params = CreateBugParams(
+            mark, 'New Bug', comment='New Bug',
+            target=ubuntu.getSourcePackage('mozilla-firefox'))
         new_bug, new_bug_event = getUtility(IBugSet).createBug(
-            CreateBugParams(mark, 'New Bug', comment='New Bug'),
-            notify_event=False)
+            params, notify_event=False)
 
         # The first message of a new bug has index 0.
         self.assertEqual(new_bug.bug_messages[0].index, 0)
 
-        getUtility(IBugTaskSet).createTask(
-            new_bug, mark, ubuntu.getSourcePackage('mozilla-firefox'))
-
         # The first task has been created and successfully nominated to Hoary.
         new_bug.addNomination(mark, ubuntu.currentseries).approve(mark)
 

=== modified file 'lib/lp/bugs/scripts/tests/test_bugimport.py'
--- lib/lp/bugs/scripts/tests/test_bugimport.py	2012-03-16 03:54:35 +0000
+++ lib/lp/bugs/scripts/tests/test_bugimport.py	2012-08-08 05:50:32 +0000
@@ -1023,12 +1023,12 @@
         firefox = getUtility(IProductSet).get(4)
         foobar = getUtility(IPersonSet).get(16)
         params = CreateBugParams(
-            title="test bug one", comment="test bug one", owner=foobar)
-        params.setBugTarget(product=firefox)
+            title="test bug one", comment="test bug one", owner=foobar,
+            target=firefox)
         test_bug_one = getUtility(IBugSet).createBug(params)
         params = CreateBugParams(
-            title="test bug two", comment="test bug two", owner=foobar)
-        params.setBugTarget(product=firefox)
+            title="test bug two", comment="test bug two", owner=foobar,
+            target=firefox)
         test_bug_two = getUtility(IBugSet).createBug(params)
         self.layer.txn.commit()
 

=== modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt'
--- lib/lp/bugs/stories/webservice/xx-bug.txt	2012-06-14 21:50:59 +0000
+++ lib/lp/bugs/stories/webservice/xx-bug.txt	2012-08-08 05:50:32 +0000
@@ -139,8 +139,8 @@
     ...     target=webservice.getAbsoluteUrl('/firefox/1.0'))
     HTTP/1.1 400 Bad Request
     ...
-    A bug target must be a Project, a Distribution or a
-    DistributionSourcePackage.  Got <ProductSeries...
+    Can't create a bug on a series. Create it with a non-series
+    task instead, and target it to the series afterwards.
 
 That operation will fail if the client doesn't specify the product or
 distribution in which the bug exists.

=== modified file 'lib/lp/bugs/tests/bug.py'
--- lib/lp/bugs/tests/bug.py	2012-07-05 00:47:31 +0000
+++ lib/lp/bugs/tests/bug.py	2012-08-08 05:50:32 +0000
@@ -28,14 +28,12 @@
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.product import IProductSet
-from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
 from lp.services.config import config
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tag_by_id,
     find_tags_by_class,
-    print_table,
     )
 
 
@@ -164,18 +162,13 @@
     distroset = getUtility(IDistributionSet)
     distribution = distroset.getByName(distribution)
 
-    # XXX: kiko 2008-02-01: would be really great if spnset consistently
-    # offered getByName.
-    spnset = getUtility(ISourcePackageNameSet)
-    sourcepackagename = spnset.queryByName(sourcepackagename)
-
     personset = getUtility(IPersonSet)
     owner = personset.getByName(owner)
 
     bugset = getUtility(IBugSet)
-    params = CreateBugParams(owner, summary, description, status=status)
-    params.setBugTarget(distribution=distribution,
-                        sourcepackagename=sourcepackagename)
+    params = CreateBugParams(
+        owner, summary, description, status=status,
+        target=distribution.getSourcePackage(sourcepackagename))
     return bugset.createBug(params)
 
 

=== modified file 'lib/lp/bugs/tests/test_bug.py'
--- lib/lp/bugs/tests/test_bug.py	2012-06-19 02:14:21 +0000
+++ lib/lp/bugs/tests/test_bug.py	2012-08-08 05:50:32 +0000
@@ -18,6 +18,7 @@
 from lp.bugs.interfaces.bugtask import (
     BugTaskImportance,
     BugTaskStatus,
+    IllegalTarget,
     UserCannotEditBugTaskAssignee,
     UserCannotEditBugTaskImportance,
     UserCannotEditBugTaskMilestone,
@@ -183,6 +184,30 @@
 
     layer = DatabaseFunctionalLayer
 
+    def test_CreateBugParams_accepts_target(self):
+        # The initial bug task's target can be set using
+        # CreateBugParams.
+        owner = self.factory.makePerson()
+        target = self.factory.makeProduct(owner=owner)
+        with person_logged_in(owner):
+            params = CreateBugParams(
+                owner=owner, title="A bug", comment="Nothing important.",
+                target=target)
+            bug = getUtility(IBugSet).createBug(params)
+            self.assertEqual(bug.default_bugtask.target, target)
+
+    def test_CreateBugParams_rejects_series_target(self):
+        # createBug refuses attempts to create a bug with a series
+        # target. A non-series task must be created first.
+        owner = self.factory.makePerson()
+        target = self.factory.makeProductSeries(owner=owner)
+        with person_logged_in(owner):
+            params = CreateBugParams(
+                owner=owner, title="A bug", comment="Nothing important.",
+                target=target)
+            self.assertRaises(
+                IllegalTarget, getUtility(IBugSet).createBug, params)
+
     def test_CreateBugParams_accepts_importance(self):
         # The importance of the initial bug task can be set using
         # CreateBugParams
@@ -191,8 +216,7 @@
         with person_logged_in(owner):
             params = CreateBugParams(
                 owner=owner, title="A bug", comment="Nothing important.",
-                importance=BugTaskImportance.HIGH)
-            params.setBugTarget(product=target)
+                importance=BugTaskImportance.HIGH, target=target)
             bug = getUtility(IBugSet).createBug(params)
             self.assertEqual(
                 bug.default_bugtask.importance, params.importance)
@@ -205,8 +229,7 @@
         with person_logged_in(owner):
             params = CreateBugParams(
                 owner=owner, title="A bug", comment="Nothing important.",
-                assignee=owner)
-            params.setBugTarget(product=target)
+                assignee=owner, target=target)
             bug = getUtility(IBugSet).createBug(params)
             self.assertEqual(
                 bug.default_bugtask.assignee, params.assignee)
@@ -219,8 +242,8 @@
         with person_logged_in(owner):
             params = CreateBugParams(
                 owner=owner, title="A bug", comment="Nothing important.",
+                target=target,
                 milestone=self.factory.makeMilestone(product=target))
-            params.setBugTarget(product=target)
             bug = getUtility(IBugSet).createBug(params)
             self.assertEqual(
                 bug.default_bugtask.milestone, params.milestone)
@@ -233,8 +256,7 @@
         with person_logged_in(owner):
             params = CreateBugParams(
                 owner=owner, title="A bug", comment="Nothing important.",
-                status=BugTaskStatus.TRIAGED)
-            params.setBugTarget(product=target)
+                status=BugTaskStatus.TRIAGED, target=target)
             bug = getUtility(IBugSet).createBug(params)
             self.assertEqual(
                 bug.default_bugtask.status, params.status)
@@ -247,8 +269,7 @@
         with person_logged_in(person):
             params = CreateBugParams(
                 owner=person, title="A bug", comment="Nothing important.",
-                importance=BugTaskImportance.HIGH)
-            params.setBugTarget(product=target)
+                importance=BugTaskImportance.HIGH, target=target)
             self.assertRaises(
                 UserCannotEditBugTaskImportance,
                 getUtility(IBugSet).createBug, params)
@@ -267,8 +288,7 @@
         with person_logged_in(person):
             params = CreateBugParams(
                 owner=person, title="A bug", comment="Nothing important.",
-                assignee=person_2)
-            params.setBugTarget(product=target)
+                assignee=person_2, target=target)
             self.assertRaises(
                 UserCannotEditBugTaskAssignee,
                 getUtility(IBugSet).createBug, params)
@@ -281,8 +301,8 @@
         with person_logged_in(person):
             params = CreateBugParams(
                 owner=person, title="A bug", comment="Nothing important.",
+                target=target,
                 milestone=self.factory.makeMilestone(product=target))
-            params.setBugTarget(product=target)
             self.assertRaises(
                 UserCannotEditBugTaskMilestone,
                 getUtility(IBugSet).createBug, params)
@@ -293,7 +313,7 @@
         person = self.factory.makePerson()
         with person_logged_in(person):
             params = CreateBugParams(
-                owner=person, title="A bug", comment="bad thing.", cve=cve)
-        params.setBugTarget(product=target)
+                owner=person, title="A bug", comment="bad thing.", cve=cve,
+                target=target)
         bug = getUtility(IBugSet).createBug(params)
         self.assertEqual([cve], [cve_link.cve for cve_link in bug.cve_links])

=== modified file 'lib/lp/bugs/tests/test_bugtarget2.py'
--- lib/lp/bugs/tests/test_bugtarget2.py	2012-01-01 02:58:52 +0000
+++ lib/lp/bugs/tests/test_bugtarget2.py	2012-08-08 05:50:32 +0000
@@ -63,6 +63,9 @@
     def test_pillar(self):
         self.assertEqual(self.bugtarget.distribution, self.bugtarget.pillar)
 
+    def test_series(self):
+        self.assertEqual(self.bugtarget, self.bugtarget.series)
+
 
 class TestProjectGroup(BugTargetBugFilingDuplicateSearchAlwaysOn,
                        TestCaseWithFactory):
@@ -163,6 +166,9 @@
     def test_pillar(self):
         self.assertEqual(self.bugtarget.product, self.bugtarget.pillar)
 
+    def test_series(self):
+        self.assertEqual(self.bugtarget, self.bugtarget.series)
+
 
 class TestSourcePackage(BugTargetBugFilingDuplicateSearchInherited,
                        TestCaseWithFactory):
@@ -189,3 +195,6 @@
     def test_pillar(self):
         self.assertEqual(
             self.bugtarget.distroseries.distribution, self.bugtarget.pillar)
+
+    def test_series(self):
+        self.assertEqual(self.bugtarget.distroseries, self.bugtarget.series)

=== modified file 'lib/lp/bugs/tests/test_errors.py'
--- lib/lp/bugs/tests/test_errors.py	2012-06-06 05:58:44 +0000
+++ lib/lp/bugs/tests/test_errors.py	2012-08-08 05:50:32 +0000
@@ -7,15 +7,9 @@
 __metaclass__ = type
 
 
-from httplib import (
-    BAD_REQUEST,
-    EXPECTATION_FAILED,
-    )
+from httplib import EXPECTATION_FAILED
 
-from lp.bugs.errors import (
-    InvalidBugTargetType,
-    InvalidDuplicateValue,
-    )
+from lp.bugs.errors import InvalidDuplicateValue
 from lp.testing import TestCase
 from lp.testing.layers import FunctionalLayer
 from lp.testing.views import create_webservice_error_view
@@ -26,10 +20,6 @@
 
     layer = FunctionalLayer
 
-    def test_InvalidBugTargetType_bad_rquest(self):
-        error_view = create_webservice_error_view(InvalidBugTargetType())
-        self.assertEqual(BAD_REQUEST, error_view.status)
-
     def test_InvalidDuplicateValue_expectation_failed(self):
         error_view = create_webservice_error_view(
             InvalidDuplicateValue("Dup"))

=== modified file 'lib/lp/bugs/tests/test_structuralsubscriptiontarget.py'
--- lib/lp/bugs/tests/test_structuralsubscriptiontarget.py	2012-06-06 16:04:34 +0000
+++ lib/lp/bugs/tests/test_structuralsubscriptiontarget.py	2012-08-08 05:50:32 +0000
@@ -32,7 +32,6 @@
     )
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.product import IProductSet
-from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.testing import verifyObject
 from lp.testing import (
@@ -526,10 +525,8 @@
 
 def distroseries_sourcepackage_filebug(distroseries, summary, status=None):
     params = CreateBugParams(
-        getUtility(ILaunchBag).user, summary, comment=summary, status=status)
-    alsa_utils = getUtility(ISourcePackageNameSet)['alsa-utils']
-    params.setBugTarget(distribution=distroseries.distribution,
-                        sourcepackagename=alsa_utils)
+        getUtility(ILaunchBag).user, summary, comment=summary, status=status,
+        target=distroseries.distribution.getSourcePackage('alsa-utils'))
     bug = distroseries.distribution.createBug(params)
     nomination = bug.addNomination(
         distroseries.distribution.owner, distroseries)

=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py	2012-07-24 06:39:54 +0000
+++ lib/lp/code/model/tests/test_branch.py	2012-08-08 05:50:32 +0000
@@ -1255,8 +1255,8 @@
     def test_bugBranchLinkDisablesDeletion(self):
         """A branch linked to a bug cannot be deleted."""
         params = CreateBugParams(
-            owner=self.user, title='Firefox bug', comment='blah')
-        params.setBugTarget(product=self.product)
+            owner=self.user, title='Firefox bug', comment='blah',
+            target=self.product)
         bug = getUtility(IBugSet).createBug(params)
         bug.linkBranch(self.branch, self.user)
         self.assertEqual(self.branch.canBeDeleted(), False,
@@ -1948,8 +1948,8 @@
         self.assertEqual(branch.date_last_modified, date_created)
 
         params = CreateBugParams(
-            owner=branch.owner, title='A bug', comment='blah')
-        params.setBugTarget(product=branch.product)
+            owner=branch.owner, title='A bug', comment='blah',
+            target=branch.product)
         bug = getUtility(IBugSet).createBug(params)
 
         bug.linkBranch(branch, branch.owner)

=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py	2012-08-03 08:02:41 +0000
+++ lib/lp/registry/model/distribution.py	2012-08-08 05:50:32 +0000
@@ -779,11 +779,6 @@
             official_candidate=official_candidate, enabled=enabled,
             whiteboard=whiteboard)
 
-    def createBug(self, bug_params):
-        """See `IBugTarget`."""
-        bug_params.setBugTarget(distribution=self)
-        return BugSet().createBug(bug_params)
-
     @property
     def currentseries(self):
         """See `IDistribution`."""

=== modified file 'lib/lp/registry/model/distributionsourcepackage.py'
--- lib/lp/registry/model/distributionsourcepackage.py	2012-08-03 01:42:13 +0000
+++ lib/lp/registry/model/distributionsourcepackage.py	2012-08-08 05:50:32 +0000
@@ -490,13 +490,6 @@
         """See `IHasBugs`."""
         return self.distribution.official_bug_tags
 
-    def createBug(self, bug_params):
-        """See `IBugTarget`."""
-        bug_params.setBugTarget(
-            distribution=self.distribution,
-            sourcepackagename=self.sourcepackagename)
-        return BugSet().createBug(bug_params)
-
     def composeCustomLanguageCodeMatch(self):
         """See `HasCustomLanguageCodesMixin`."""
         return And(

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2012-08-03 01:42:13 +0000
+++ lib/lp/registry/model/distroseries.py	2012-08-08 05:50:32 +0000
@@ -257,6 +257,11 @@
         return self.distribution
 
     @property
+    def series(self):
+        """See `ISeriesBugTarget`."""
+        return self
+
+    @property
     def named_version(self):
         return '%s (%s)' % (self.displayname, self.version)
 
@@ -1460,20 +1465,6 @@
             self, created_since_date, status, archive, pocket, custom_type,
             name=name, version=version, exact_match=exact_match)
 
-    def createBug(self, bug_params):
-        """See `IBugTarget`."""
-        # We don't currently support opening a new bug on an IDistroSeries,
-        # because internally bugs are reported against IDistroSeries only when
-        # targeted to be fixed in that series, which is rarely the case for a
-        # brand new bug report.
-        raise NotImplementedError(
-            "A new bug cannot be filed directly on a distribution series, "
-            "because series are meant for \"targeting\" a fix to a specific "
-            "version. It's possible that we may change this behaviour to "
-            "allow filing a bug on a distribution series in the "
-            "not-too-distant future. For now, you probably meant to file "
-            "the bug on the distribution instead.")
-
     def getBugSummaryContextWhereClause(self):
         """See BugTargetBase."""
         # Circular fail.

=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py	2012-08-03 08:02:41 +0000
+++ lib/lp/registry/model/product.py	2012-08-08 05:50:32 +0000
@@ -1032,11 +1032,6 @@
             """ % sqlvalues(self.id, name))
         return results
 
-    def createBug(self, bug_params):
-        """See `IBugTarget`."""
-        bug_params.setBugTarget(product=self)
-        return BugSet().createBug(bug_params)
-
     def getBugSummaryContextWhereClause(self):
         """See BugTargetBase."""
         # Circular fail.

=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py	2012-08-03 01:42:13 +0000
+++ lib/lp/registry/model/productseries.py	2012-08-08 05:50:32 +0000
@@ -163,6 +163,11 @@
         return self.product
 
     @property
+    def series(self):
+        """See `ISeriesBugTarget`."""
+        return self
+
+    @property
     def answers_usage(self):
         """See `IServiceUsage.`"""
         return self.product.answers_usage
@@ -440,10 +445,6 @@
         """See `IHasBugs`."""
         return self.product.official_bug_tags
 
-    def createBug(self, bug_params):
-        """See IBugTarget."""
-        raise NotImplementedError('Cannot file a bug against a productseries')
-
     def getBugSummaryContextWhereClause(self):
         """See BugTargetBase."""
         # Circular fail.

=== modified file 'lib/lp/registry/model/sourcepackage.py'
--- lib/lp/registry/model/sourcepackage.py	2012-08-03 01:42:13 +0000
+++ lib/lp/registry/model/sourcepackage.py	2012-08-08 05:50:32 +0000
@@ -524,25 +524,16 @@
         """See `IHasOwner`."""
         return self.distroseries.owner
 
-    def createBug(self, bug_params):
-        """See `IBugTarget`."""
-        # We don't currently support opening a new bug directly on an
-        # ISourcePackage, because internally ISourcePackage bugs mean bugs
-        # targeted to be fixed in a specific distroseries + sourcepackage.
-        raise NotImplementedError(
-            "A new bug cannot be filed directly on a source package in a "
-            "specific distribution series, because series are meant for "
-            "\"targeting\" a fix to a specific series. It's possible that "
-            "we may change this behaviour to allow filing a bug on a "
-            "distribution series source package in the not-too-distant "
-            "future. For now, you probably meant to file the bug on the "
-            "distro-wide (i.e. not series-specific) source package.")
-
     @property
     def pillar(self):
         """See `IBugTarget`."""
         return self.distroseries.distribution
 
+    @property
+    def series(self):
+        """See `ISeriesBugTarget`."""
+        return self.distroseries
+
     def getBugSummaryContextWhereClause(self):
         """See BugTargetBase."""
         # Circular fail.

=== modified file 'lib/lp/systemhomes.py'
--- lib/lp/systemhomes.py	2012-08-07 22:51:16 +0000
+++ lib/lp/systemhomes.py	2012-08-08 05:50:32 +0000
@@ -26,7 +26,6 @@
 from zope.interface import implements
 
 from lp.bugs.adapters.bug import convert_to_information_type
-from lp.bugs.errors import InvalidBugTargetType
 from lp.bugs.interfaces.bug import (
     CreateBugParams,
     IBugSet,
@@ -57,16 +56,9 @@
     ParameterError,
     )
 from lp.registry.enums import PRIVATE_INFORMATION_TYPES
-from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.distributionsourcepackage import (
-    IDistributionSourcePackage,
-    )
 from lp.registry.interfaces.distroseries import IDistroSeriesSet
 from lp.registry.interfaces.mailinglist import IMailingListApplication
-from lp.registry.interfaces.product import (
-    IProduct,
-    IProductSet,
-    )
+from lp.registry.interfaces.product import IProductSet
 from lp.services.config import config
 from lp.services.database.lpstorm import IStore
 from lp.services.feeds.interfaces.application import IFeedsApplication
@@ -165,18 +157,7 @@
                 private, security_related)
         params = CreateBugParams(
             title=title, comment=description, owner=owner,
-            information_type=information_type, tags=tags)
-        if IProduct.providedBy(target):
-            params.setBugTarget(product=target)
-        elif IDistribution.providedBy(target):
-            params.setBugTarget(distribution=target)
-        elif IDistributionSourcePackage.providedBy(target):
-            params.setBugTarget(distribution=target.distribution,
-                                sourcepackagename=target.sourcepackagename)
-        else:
-            raise InvalidBugTargetType(
-                "A bug target must be a Project, a Distribution or a "
-                "DistributionSourcePackage. Got %r." % target)
+            information_type=information_type, tags=tags, target=target)
         return getUtility(IBugSet).createBug(params)
 
     @property

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2012-08-01 05:47:07 +0000
+++ lib/lp/testing/factory.py	2012-08-08 05:50:32 +0000
@@ -1708,13 +1708,13 @@
             self.makeSourcePackagePublishingHistory(
                 distroseries=distribution.currentseries,
                 sourcepackagename=sourcepackagename)
+        target = product or distribution
+        if sourcepackagename is not None:
+            target = target.getSourcePackageName(sourcepackagename)
         create_bug_params = CreateBugParams(
             owner, title, comment=comment, information_type=information_type,
             datecreated=date_created, description=description,
-            status=status, tags=tags)
-        create_bug_params.setBugTarget(
-            product=product, distribution=distribution,
-            sourcepackagename=sourcepackagename)
+            status=status, tags=tags, target=target)
         bug = getUtility(IBugSet).createBug(create_bug_params)
         if bug_watch_url is not None:
             # fromText() creates a bug watch associated with the bug.


Follow ups