← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/delete-nullbugtask into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/delete-nullbugtask into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #365489 in Launchpad itself: "Remove NullBugTask"
  https://bugs.launchpad.net/launchpad/+bug/365489

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/delete-nullbugtask/+merge/53367

This branch removes NullBugTask's final callsite, using determine_target in validate_target_attribute instead.

With that gone, NullBugTask and its tests have also been deleted.
-- 
https://code.launchpad.net/~wgrant/launchpad/delete-nullbugtask/+merge/53367
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/delete-nullbugtask into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bugnomination.py'
--- lib/lp/bugs/browser/bugnomination.py	2011-02-02 15:43:31 +0000
+++ lib/lp/bugs/browser/bugnomination.py	2011-03-15 04:26:27 +0000
@@ -15,10 +15,7 @@
 
 import pytz
 from zope.component import getUtility
-from zope.publisher.interfaces import (
-    implements,
-    NotFound,
-    )
+from zope.publisher.interfaces import implements
 
 from canonical.launchpad import _
 from canonical.launchpad.webapp import (
@@ -41,7 +38,6 @@
     IBugNomination,
     IBugNominationForm,
     )
-from lp.bugs.interfaces.bugtask import INullBugTask
 from lp.bugs.interfaces.cve import ICveSet
 
 
@@ -65,10 +61,6 @@
         LaunchpadFormView.__init__(self, context, request)
 
     def initialize(self):
-        if INullBugTask.providedBy(self.current_bugtask):
-            # It shouldn't be possible to nominate a bug that hasn't
-            # been reported yet.
-            raise NotFound(self.current_bugtask, '+nominate', self.request)
         LaunchpadFormView.initialize(self)
         # Update the submit label based on the user's permission.
         submit_action = self.__class__.actions.byname['actions.submit']

=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py	2011-03-10 16:15:57 +0000
+++ lib/lp/bugs/browser/bugtask.py	2011-03-15 04:26:27 +0000
@@ -236,7 +236,6 @@
     IDistroSeriesBugTask,
     IFrontPageBugTaskSearch,
     INominationsReviewTableBatchNavigator,
-    INullBugTask,
     IPersonBugTaskSearch,
     IProductSeriesBugTask,
     IRemoveQuestionFromBugTaskForm,
@@ -490,7 +489,7 @@
         """Return the IBugTask for this name in this context.
 
         If the bug has been reported, but not in this specific context, a
-        NullBugTask will be returned.
+        redirect to the default context will be returned.
 
         Raises NotFoundError if no bug with the given name is found.
 
@@ -514,59 +513,15 @@
                 # Security proxy this object on the way out.
                 return getUtility(IBugTaskSet).get(bugtask.id)
 
-        # If we've come this far, it means that no actual task exists in this
-        # context, so we'll return a null bug task. This makes it possible to,
-        # for example, return a bug page for a context in which the bug hasn't
-        # yet been reported.
-        if IProduct.providedBy(context):
-            null_bugtask = bug.getNullBugTask(product=context)
-        elif IProductSeries.providedBy(context):
-            null_bugtask = bug.getNullBugTask(productseries=context)
-        elif IDistribution.providedBy(context):
-            null_bugtask = bug.getNullBugTask(distribution=context)
-        elif IDistributionSourcePackage.providedBy(context):
-            null_bugtask = bug.getNullBugTask(
-                distribution=context.distribution,
-                sourcepackagename=context.sourcepackagename)
-        elif IDistroSeries.providedBy(context):
-            null_bugtask = bug.getNullBugTask(distroseries=context)
-        elif ISourcePackage.providedBy(context):
-            null_bugtask = bug.getNullBugTask(
-                distroseries=context.distroseries,
-                sourcepackagename=context.sourcepackagename)
-        else:
-            raise TypeError(
-                "Unknown context type for bug task: %s" % repr(context))
-
-        return null_bugtask
+        # If we've come this far, there's no task for the requested
+        # context. Redirect to one that exists.
+        return self.redirectSubTree(canonical_url(bug.default_bugtask))
 
 
 class BugTaskNavigation(Navigation):
     """Navigation for the `IBugTask`."""
     usedfor = IBugTask
 
-    def traverse(self, name):
-        """Traverse the `IBugTask`."""
-        # Are we traversing to the view or edit status page of the
-        # bugtask? If so, and the task actually exists, return the
-        # appropriate page. If the task doesn't yet exist (i.e. it's a
-        # NullBugTask), then return a 404. In other words, the URL:
-        #
-        #   /products/foo/+bug/1/+viewstatus
-        #
-        # will return the +viewstatus page if bug 1 has actually been
-        # reported in "foo". If bug 1 has not yet been reported in "foo",
-        # a 404 will be returned.
-        if name not in ("+viewstatus", "+editstatus"):
-            # You're going in the wrong direction.
-            return None
-        if INullBugTask.providedBy(self.context):
-            # The bug has not been reported in this context.
-            return None
-        # Yes! The bug has been reported in this context.
-        return getMultiAdapter((self.context, self.request),
-            name=(name + "-page"))
-
     @stepthrough('attachments')
     def traverse_attachments(self, name):
         """traverse to an attachment by id."""
@@ -655,13 +610,8 @@
 
     @property
     def page_title(self):
-        bugtask = self.context
-        if INullBugTask.providedBy(bugtask):
-            heading = 'Bug #%s is not in %s' % (
-                bugtask.bug.id, bugtask.bugtargetdisplayname)
-        else:
-            heading = 'Bug #%s in %s' % (
-                bugtask.bug.id, bugtask.bugtargetdisplayname)
+        heading = 'Bug #%s in %s' % (
+            self.context.bug.id, self.context.bugtargetdisplayname)
         return smartquote('%s: "%s"') % (heading, self.context.bug.title)
 
     @property
@@ -698,12 +648,6 @@
         # See render() for how this flag is used.
         self._redirecting_to_bug_list = False
 
-        # If the bug is not reported in this context, redirect
-        # to the default bug task.
-        if not self.isReportedInContext():
-            self.request.response.redirect(
-                canonical_url(self.context.bug.default_bugtask))
-
         self.bug_title_edit_widget = TextLineEditorWidget(
             bug, IBug['title'], "Edit this summary", 'h1',
             edit_url=canonical_url(self.context, view_name='+edit'))
@@ -741,69 +685,6 @@
                 series.bugtargetdisplayname)
         self.request.response.redirect(canonical_url(self.context))
 
-    def reportBugInContext(self):
-        """Report the bug affects the current context."""
-        fake_task = self.context
-        if self.request.form.get("reportbug"):
-            if self.isReportedInContext():
-                self.notices.append(
-                    "The bug is already reported in this context.")
-                return
-            # The user has requested that the bug be reported in this
-            # context.
-            if IUpstreamBugTask.providedBy(fake_task):
-                # Create a real upstream task in this context.
-                real_task = fake_task.bug.addTask(
-                    getUtility(ILaunchBag).user, fake_task.product)
-            elif IDistroBugTask.providedBy(fake_task):
-                # Create a real distro bug task in this context.
-                real_task = fake_task.bug.addTask(
-                    getUtility(ILaunchBag).user, fake_task.target)
-            elif IDistroSeriesBugTask.providedBy(fake_task):
-                self._nominateBug(fake_task.distroseries)
-                return
-            elif IProductSeriesBugTask.providedBy(fake_task):
-                self._nominateBug(fake_task.productseries)
-                return
-            else:
-                raise TypeError(
-                    "Unknown bug task type: %s" % repr(fake_task))
-
-            self.context = real_task
-
-            # Add an appropriate feedback message
-            self.notices.append("Thank you for your bug report.")
-
-    def isReportedInContext(self):
-        """Is the bug reported in this context? Returns True or False.
-
-        It considers a nominated bug to be reported.
-
-        This is particularly useful for views that may render a
-        NullBugTask.
-        """
-        if self.context.id is not None:
-            # Fast path for real bugtasks: they have a DB id.
-            return True
-        params = BugTaskSearchParams(user=self.user, bug=self.context.bug)
-        matching_bugtasks = self.context.target.searchTasks(params)
-        if self.context.productseries is not None:
-            nomination_target = self.context.productseries
-        elif self.context.distroseries is not None:
-            nomination_target = self.context.distroseries
-        else:
-            nomination_target = None
-        if nomination_target is not None:
-            try:
-                nomination = self.context.bug.getNominationFor(
-                    nomination_target)
-            except NotFoundError:
-                nomination = None
-        else:
-            nomination = None
-
-        return nomination is not None or matching_bugtasks.count() > 0
-
     def isSeriesTargetableContext(self):
         """Is the context something that supports Series targeting?
 
@@ -1744,17 +1625,12 @@
         The assignee is included.
         """
         bugtask = self.context
-
-        if INullBugTask.providedBy(bugtask):
-            return u"Not reported in %s" % bugtask.bugtargetname
-
         assignee = bugtask.assignee
         status = bugtask.status
         status_title = status.title.capitalize()
 
         if not assignee:
             return status_title + ' (unassigned)'
-
         assignee_html = PersonFormatterAPI(assignee).link('+assignedbugs')
 
         if status in (BugTaskStatus.INVALID,

=== modified file 'lib/lp/bugs/browser/configure.zcml'
--- lib/lp/bugs/browser/configure.zcml	2011-03-11 21:31:10 +0000
+++ lib/lp/bugs/browser/configure.zcml	2011-03-15 04:26:27 +0000
@@ -637,35 +637,27 @@
             class="lp.bugs.browser.bugalsoaffects.BugAlsoAffectsDistroMetaView"
             permission="launchpad.AnyPerson"/>
         <browser:page
-            name="+editstatus-page"
-            for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
-            class="lp.bugs.browser.bugtask.BugTaskEditView"
-            permission="launchpad.Edit"
-            template="../templates/bugtask-edit.pt">
-                </browser:page>
-        <browser:page
-            name="+edit-form"
-            for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
-            class="lp.bugs.browser.bugtask.BugTaskEditView"
-            permission="launchpad.Edit"
-            template="../templates/bugtask-edit-form.pt">
-                </browser:page>
-        <browser:page
-            name="+editstatus-page"
-            for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
-            class="lp.bugs.browser.bugtask.BugTaskEditView"
-            permission="launchpad.Edit"
-            template="../templates/bugtask-edit.pt">
-                </browser:page>
-        <browser:page
-            name="+edit-form"
-            for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
-            class="lp.bugs.browser.bugtask.BugTaskEditView"
-            permission="launchpad.Edit"
-            template="../templates/bugtask-edit-form.pt">
-                </browser:page>
-        <browser:page
-            name="+viewstatus-page"
+            name="+edit-form"
+            for="lp.bugs.interfaces.bugtask.IUpstreamBugTask"
+            class="lp.bugs.browser.bugtask.BugTaskEditView"
+            permission="launchpad.Edit"
+            template="../templates/bugtask-edit-form.pt">
+                </browser:page>
+        <browser:page
+            name="+edit-form"
+            for="lp.bugs.interfaces.bugtask.IProductSeriesBugTask"
+            class="lp.bugs.browser.bugtask.BugTaskEditView"
+            permission="launchpad.Edit"
+            template="../templates/bugtask-edit-form.pt">
+                </browser:page>
+        <browser:page
+            name="+editstatus"
+            for="lp.bugs.interfaces.bugtask.IBugTask"
+            class="lp.bugs.browser.bugtask.BugTaskEditView"
+            permission="launchpad.Edit"
+            template="../templates/bugtask-edit.pt"/>
+        <browser:page
+            name="+viewstatus"
             for="lp.bugs.interfaces.bugtask.IBugTask"
             class="lp.bugs.browser.bugtask.BugTaskStatusView"
             permission="launchpad.View"
@@ -680,13 +672,6 @@
             for="lp.bugs.interfaces.bugtask.IDistroBugTask"
             name="+index"/>
         <browser:page
-            name="+editstatus-page"
-            for="lp.bugs.interfaces.bugtask.IDistroBugTask"
-            class="lp.bugs.browser.bugtask.BugTaskEditView"
-            permission="launchpad.Edit"
-            template="../templates/bugtask-edit.pt">
-                </browser:page>
-        <browser:page
             name="+edit-form"
             for="lp.bugs.interfaces.bugtask.IDistroBugTask"
             class="lp.bugs.browser.bugtask.BugTaskEditView"
@@ -697,13 +682,6 @@
             for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
             name="+index"/>
         <browser:page
-            name="+editstatus-page"
-            for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
-            class="lp.bugs.browser.bugtask.BugTaskEditView"
-            permission="launchpad.Edit"
-            template="../templates/bugtask-edit.pt">
-                </browser:page>
-        <browser:page
             name="+edit-form"
             for="lp.bugs.interfaces.bugtask.IDistroSeriesBugTask"
             class="lp.bugs.browser.bugtask.BugTaskEditView"

=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
--- lib/lp/bugs/browser/tests/bug-views.txt	2011-02-15 09:31:20 +0000
+++ lib/lp/bugs/browser/tests/bug-views.txt	2011-03-15 04:26:27 +0000
@@ -777,7 +777,7 @@
     ...         'testproduct.actions.save': 'Save Changes',
     ...         })
     >>> view = getMultiAdapter(
-    ...     (bug.bugtasks[0], request), name="+editstatus-page")
+    ...     (bug.bugtasks[0], request), name="+editstatus")
     >>> view.initialize()
 
     >>> view = getMultiAdapter(
@@ -833,7 +833,7 @@
     ...         'testproduct.actions.save': 'Save Changes',
     ...         })
     >>> view = getMultiAdapter(
-    ...     (bug.bugtasks[0], request), name="+editstatus-page")
+    ...     (bug.bugtasks[0], request), name="+editstatus")
     >>> view.initialize()
 
     >>> view = getMultiAdapter(

=== modified file 'lib/lp/bugs/browser/tests/bugtask-edit-views.txt'
--- lib/lp/bugs/browser/tests/bugtask-edit-views.txt	2010-10-18 22:24:59 +0000
+++ lib/lp/bugs/browser/tests/bugtask-edit-views.txt	2011-03-15 04:26:27 +0000
@@ -29,7 +29,7 @@
     ...        ubuntu_thunderbird_task.sourcepackagename.name}
     >>> request = LaunchpadTestRequest(method='POST', form=edit_form)
     >>> edit_view = getMultiAdapter(
-    ...     (ubuntu_thunderbird_task, request), name='+editstatus-page')
+    ...     (ubuntu_thunderbird_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> ubuntu_thunderbird_task.status.title
     'In Progress'
@@ -50,7 +50,7 @@
     >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'linux-2.6.12'
     >>> request = LaunchpadTestRequest(method='POST', form=edit_form)
     >>> edit_view = getMultiAdapter(
-    ...     (ubuntu_thunderbird_task, request), name='+editstatus-page')
+    ...     (ubuntu_thunderbird_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> ubuntu_thunderbird_task.sourcepackagename.name
     u'linux-source-2.6.15'
@@ -73,7 +73,7 @@
     >>> edit_form['ubuntu_thunderbird.sourcepackagename'] = u'no-such-package'
     >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
     >>> edit_view = getMultiAdapter(
-    ...     (ubuntu_thunderbird_task, request), name='+editstatus-page')
+    ...     (ubuntu_thunderbird_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> for error in edit_view.errors:
     ...     print error
@@ -112,7 +112,7 @@
     ...     }
     >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
     >>> edit_view = getMultiAdapter(
-    ...     (ubuntu_task, request), name='+editstatus-page')
+    ...     (ubuntu_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> for error in edit_view.errors:
     ...     print error
@@ -144,7 +144,7 @@
     ...     }
     >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
     >>> edit_view = getMultiAdapter(
-    ...     (ubuntu_grumpy_task, request), name='+editstatus-page')
+    ...     (ubuntu_grumpy_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> for error in edit_view.errors:
     ...     print error
@@ -155,7 +155,7 @@
 
 == Edit the Product ==
 
-+editstatus-page allows a bug to be retargeted to another product.
++editstatus allows a bug to be retargeted to another product.
 
     >>> bug_seven = getUtility(IBugSet).get(7)
     >>> product_task = bug_seven.bugtasks[0]
@@ -173,7 +173,7 @@
     ...     }
     >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
     >>> edit_view = getMultiAdapter(
-    ...     (product_task, request), name='+editstatus-page')
+    ...     (product_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> [str(error) for error in edit_view.errors]
     []
@@ -193,7 +193,7 @@
     ...     }
     >>> request = LaunchpadTestRequest(form=edit_form, method='POST')
     >>> edit_view = getMultiAdapter(
-    ...     (product_task, request), name='+editstatus-page')
+    ...     (product_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> for error in edit_view.errors:
     ...     print error
@@ -235,7 +235,7 @@
     ...         'thunderbird.importance': 'Critical',
     ...         'thunderbird.bugwatch': '6'})
     >>> edit_view = getMultiAdapter(
-    ...     (thunderbird_task, request), name='+editstatus-page')
+    ...     (thunderbird_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> thunderbird_task.bugwatch == bugzilla_watch
     True
@@ -251,7 +251,7 @@
     ...         'thunderbird.actions.save': 'Save Changes',
     ...         'thunderbird.bugwatch-empty-marker': '1'})
     >>> edit_view = getMultiAdapter(
-    ...     (thunderbird_task, request), name='+editstatus-page')
+    ...     (thunderbird_task, request), name='+editstatus')
     >>> edit_view.initialize()
     >>> thunderbird_task.bugwatch is None
     True
@@ -280,7 +280,7 @@
     >>> request = LaunchpadTestRequest()
     >>> ubuntu_task = getUtility(IBugTaskSet).get(17)
     >>> bugtask_edit_view = getMultiAdapter(
-    ...     (ubuntu_task, request), name="+editstatus-page")
+    ...     (ubuntu_task, request), name="+editstatus")
     >>> bugtask_edit_view.initialize()
 
     >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])
@@ -293,7 +293,7 @@
     >>> login("no-priv@xxxxxxxxxxxxx")
 
     >>> bugtask_edit_view = getMultiAdapter(
-    ...     (ubuntu_task, request), name="+editstatus-page")
+    ...     (ubuntu_task, request), name="+editstatus")
     >>> bugtask_edit_view.initialize()
 
     >>> isinstance(bugtask_edit_view.widgets['milestone'], ItemDisplayWidget)
@@ -312,7 +312,7 @@
 Unlike before, no-priv can now edit the milestone.
 
     >>> bugtask_edit_view = getMultiAdapter(
-    ...     (ubuntu_task, request), name="+editstatus-page")
+    ...     (ubuntu_task, request), name="+editstatus")
     >>> bugtask_edit_view.initialize()
 
     >>> IInputWidget.providedBy(bugtask_edit_view.widgets['milestone'])

=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml	2011-03-08 01:56:34 +0000
+++ lib/lp/bugs/configure.zcml	2011-03-15 04:26:27 +0000
@@ -302,18 +302,6 @@
             factory="lp.bugs.browser.bugcomment.BugCommentBreadcrumb"
             permission="zope.Public"/>
 
-        <!-- NullBugTask -->
-
-        <class
-            class="lp.bugs.model.bugtask.NullBugTask">
-            <require
-                permission="launchpad.View"
-                interface="lp.bugs.interfaces.bugtask.IBugTask"/>
-            <require
-                permission="launchpad.Edit"
-                set_schema="lp.bugs.interfaces.bugtask.IBugTask"/>
-        </class>
-
         <!-- BugTaskSearchParams -->
         <class
             class="lp.bugs.interfaces.bugtask.BugTaskSearchParams">
@@ -691,7 +679,6 @@
                     getDirectSubscribers
                     getDirectSubscriptions
                     getIndirectSubscribers
-                    getNullBugTask
                     is_complete
                     official_tags
                     who_made_private

=== modified file 'lib/lp/bugs/doc/bugtask.txt'
--- lib/lp/bugs/doc/bugtask.txt	2011-02-14 11:04:09 +0000
+++ lib/lp/bugs/doc/bugtask.txt	2011-03-15 04:26:27 +0000
@@ -732,83 +732,6 @@
     False
 
 
-== Null Bug Tasks ==
-
-Sometimes we need to be able to render a page for a bug in a context,
-when the bug hasn't actually been filed yet in that context. For cases
-like these, use the NullBugTask object.
-
-
-    >>> from lp.bugs.interfaces.bugtask import INullBugTask
-    >>> netapplet = productset.get(11)
-    >>> null_bugtask = bug_one.getNullBugTask(product=netapplet)
-    >>> verifyObject(INullBugTask, null_bugtask)
-    True
-    >>> IUpstreamBugTask.providedBy(null_bugtask)
-    True
-
-    >>> null_bugtask.id is None
-    True
-    >>> null_bugtask.title
-    u'Bug #1 is not in NetApplet: "Firefox does not support SVG"'
-    >>> null_bugtask.product is netapplet
-    True
-    >>> null_bugtask.bug == bug_one
-    True
-    >>> null_bugtask.datecreated is None
-    True
-    >>> null_bugtask.date_assigned is None
-    True
-    >>> null_bugtask.age is None
-    True
-    >>> null_bugtask.status is None
-    True
-    >>> null_bugtask.sourcepackagename is None
-    True
-    >>> null_bugtask.distribution is None
-    True
-    >>> null_bugtask.distroseries is None
-    True
-    >>> null_bugtask.milestone is None
-    True
-    >>> null_bugtask.importance is None
-    True
-    >>> null_bugtask.assignee is None
-    True
-    >>> null_bugtask.bugwatch is None
-    True
-    >>> null_bugtask.owner is None
-    True
-    >>> null_bugtask.target == netapplet
-    True
-    >>> null_bugtask.bugtargetname
-    u'netapplet'
-    >>> expected_related_task_ids = [
-    ...     task.id for task in null_bugtask.related_tasks]
-    >>> actual_related_task_ids = [task.id for task in bug_one.bugtasks]
-    >>> expected_related_task_ids.sort()
-    >>> actual_related_task_ids.sort()
-    >>> expected_related_task_ids == actual_related_task_ids
-    True
-
-    >>> null_bugtask.conjoined_slave is None
-    True
-    >>> null_bugtask.conjoined_master is None
-    True
-
-The astute reader will have noticed that NullBugTask automatically
-"marked" itself as providing the correct IBugTask interface. Let's see
-two more examples:
-
-    >>> ubuntu_null_bugtask = bug_one.getNullBugTask(distribution=ubuntu)
-    >>> IDistroBugTask.providedBy(ubuntu_null_bugtask)
-    True
-
-    >>> warty_null_bugtask = bug_one.getNullBugTask(distroseries=warty)
-    >>> IDistroSeriesBugTask.providedBy(warty_null_bugtask)
-    True
-
-
 = Bug Privacy =
 
 A bug is either private or public. Private bugs are only visible (e.g. in search
@@ -1080,6 +1003,7 @@
 the latter is not exposed in `IBugTask`, so the `bugtargetdisplayname`
 is used here.
 
+    >>> netapplet = productset.get(11)
     >>> upstream_task = bugtaskset.createTask(
     ...     bug=bug_one, product=netapplet, owner=mark,
     ...     status=STATUS_NEW, importance=IMPORTANCE_MEDIUM)

=== modified file 'lib/lp/bugs/doc/displaying-bugs-and-tasks.txt'
--- lib/lp/bugs/doc/displaying-bugs-and-tasks.txt	2010-11-01 15:46:48 +0000
+++ lib/lp/bugs/doc/displaying-bugs-and-tasks.txt	2011-03-15 04:26:27 +0000
@@ -150,17 +150,6 @@
   >>> render_bugtask_status(test_task)
   u'Fix released, assigned to ...Foo Bar...'
 
-This code also works for null bug tasks:
-
-  >>> from lp.bugs.model.bug import Bug
-  >>> from lp.bugs.model.bugtask import NullBugTask
-  >>> from lp.registry.model.product import Product
-  >>> bug_one = Bug.get(1)
-  >>> netapplet = Product.selectOneBy(name="netapplet")
-  >>> null_bugtask = NullBugTask(bug=bug_one, product=netapplet)
-  >>> render_bugtask_status(null_bugtask)
-  u'Not reported in netapplet'
-
 Lastly, some cleanup:
 
   >>> test_task.transitionToStatus(
@@ -206,9 +195,3 @@
 
   >>> related_task.transitionToStatus(
   ...   ORIGINAL_STATUS, getUtility(ILaunchBag).user)
-
-Null tasks are also supported:
-
-  >>> render_bugtask_status_elsewhere(null_bugtask)
-  'filed in 3 other places'
-

=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py	2011-03-08 01:56:34 +0000
+++ lib/lp/bugs/interfaces/bug.py	2011-03-15 04:26:27 +0000
@@ -721,11 +721,6 @@
             returned rows. The step parameter in each slice is ignored.
         """
 
-    def getNullBugTask(product=None, productseries=None,
-                    sourcepackagename=None, distribution=None,
-                    distroseries=None):
-        """Create an INullBugTask and return it for the given parameters."""
-
     @operation_parameters(
         target=Reference(schema=Interface, title=_('Target')))
     @call_with(owner=REQUEST_USER)

=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py	2011-03-07 21:05:12 +0000
+++ lib/lp/bugs/interfaces/bugtask.py	2011-03-15 04:26:27 +0000
@@ -30,7 +30,6 @@
     'IDistroSeriesBugTask',
     'IFrontPageBugTaskSearch',
     'INominationsReviewTableBatchNavigator',
-    'INullBugTask',
     'IPersonBugTaskSearch',
     'IProductSeriesBugTask',
     'IRemoveQuestionFromBugTaskForm',
@@ -839,16 +838,6 @@
 IBugWatch['bugtasks'].value_type.schema = IBugTask
 
 
-class INullBugTask(IBugTask):
-    """A marker interface for an IBugTask that doesn't exist in a context.
-
-    An INullBugTask is useful when wanting to view a bug in a context
-    where that bug hasn't yet been reported. This might happen, for
-    example, when searching to see if a bug you want to report has
-    already been filed and finding matching reports that don't yet
-    have tasks reported in your context.
-    """
-
 UPSTREAM_STATUS_VOCABULARY = SimpleVocabulary(
     [SimpleTerm(
         "pending_bugwatch",

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py	2011-03-08 01:56:34 +0000
+++ lib/lp/bugs/model/bug.py	2011-03-15 04:26:27 +0000
@@ -168,7 +168,6 @@
     BugTask,
     bugtask_sort_key,
     get_bug_privacy_filter,
-    NullBugTask,
     )
 from lp.bugs.model.bugwatch import BugWatch
 from lp.bugs.model.structuralsubscription import (
@@ -1429,16 +1428,6 @@
                 need_validity=True))
         return DecoratedResultSet(result, pre_iter_hook=eager_load_owners)
 
-    def getNullBugTask(self, product=None, productseries=None,
-                    sourcepackagename=None, distribution=None,
-                    distroseries=None):
-        """See `IBug`."""
-        return NullBugTask(bug=self, product=product,
-                           productseries=productseries,
-                           sourcepackagename=sourcepackagename,
-                           distribution=distribution,
-                           distroseries=distroseries)
-
     def addNomination(self, owner, target):
         """See `IBug`."""
         if not self.canBeNominatedFor(target):

=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py	2011-03-11 03:06:43 +0000
+++ lib/lp/bugs/model/bugtask.py	2011-03-15 04:26:27 +0000
@@ -13,7 +13,6 @@
     'BugTaskMixin',
     'BugTask',
     'BugTaskSet',
-    'NullBugTask',
     'bugtask_sort_key',
     'determine_target',
     'get_bug_privacy_filter',
@@ -119,7 +118,6 @@
     IDistroSeriesBugTask,
     IllegalRelatedBugTasksParams,
     IllegalTarget,
-    INullBugTask,
     IProductSeriesBugTask,
     IUpstreamBugTask,
     RESOLVED_BUGTASK_STATUSES,
@@ -374,68 +372,6 @@
         return sorted(result, key=pillar_sort_key)
 
 
-class NullBugTask(BugTaskMixin):
-    """A null object for IBugTask.
-
-    This class is used, for example, to be able to render a URL like:
-
-      /products/evolution/+bug/5
-
-    when bug #5 isn't yet reported in evolution.
-    """
-    implements(INullBugTask)
-
-    def __init__(self, bug, product=None, productseries=None,
-                 sourcepackagename=None, distribution=None,
-                 distroseries=None):
-        """Initialize a NullBugTask."""
-        self.id = None
-        self.bug = bug
-        self.product = product
-        self.productseries = productseries
-        self.sourcepackagename = sourcepackagename
-        self.distribution = distribution
-        self.distroseries = distroseries
-
-        # Mark the task with the correct interface, depending on its
-        # context.
-        if self.product:
-            alsoProvides(self, IUpstreamBugTask)
-        elif self.distribution:
-            alsoProvides(self, IDistroBugTask)
-        elif self.distroseries:
-            alsoProvides(self, IDistroSeriesBugTask)
-        elif self.productseries:
-            alsoProvides(self, IProductSeriesBugTask)
-        else:
-            raise AssertionError('Unknown NullBugTask: %r.' % self)
-
-        # Make us provide the interface by setting all required attributes
-        # to None, and define the methods as raising NotImplementedError.
-        # The attributes are set to None because it doesn't make
-        # sense for these attributes to have a value when there is no
-        # real task there. (In fact, it may make sense for these
-        # values to be non-null, but I haven't yet found a use case
-        # for it, and I don't think there's any point on designing for
-        # that until we've encountered one.)
-        def this_is_a_null_bugtask_method(*args, **kwargs):
-            raise NotImplementedError
-
-        for name, spec in INullBugTask.namesAndDescriptions(True):
-            if not hasattr(self, name):
-                if IMethod.providedBy(spec):
-                    value = this_is_a_null_bugtask_method
-                else:
-                    value = None
-                setattr(self, name, value)
-
-    @property
-    def title(self):
-        """See `IBugTask`."""
-        return 'Bug #%s is not in %s: "%s"' % (
-            self.bug.id, self.bugtargetdisplayname, self.bug.title)
-
-
 def BugTaskToBugAdapter(bugtask):
     """Adapt an IBugTask to an IBug."""
     return bugtask.bug
@@ -467,9 +403,10 @@
     else:
         target_params[attr[:-2]] = getUtility(utility_iface).get(value)
 
-    # Use a NullBugTask to determine the new target.
-    nulltask = NullBugTask(self.bug, **target_params)
-    self.updateTargetNameCache(nulltask.target)
+    # Update the target name cache with the potential new target. The
+    # attribute changes haven't been made yet, so we need to calculate the
+    # target manually.
+    self.updateTargetNameCache(determine_target(**target_params))
 
     return value
 

=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
--- lib/lp/bugs/templates/bugtask-index.pt	2011-02-25 22:09:46 +0000
+++ lib/lp/bugs/templates/bugtask-index.pt	2011-03-15 04:26:27 +0000
@@ -72,7 +72,6 @@
       <div id="tags-autocomplete">
        <div id="tags-autocomplete-content"></div>
       </div>
-      <tal:block condition="view/reportBugInContext" />
 
       <p class="informational message"
          tal:condition="view/notices"