Merge lp:~cjwatson/launchpad/productseries-ubuntupkg-dsp-vocab into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18195
Proposed branch: lp:~cjwatson/launchpad/productseries-ubuntupkg-dsp-vocab
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/distribution-filebug-dsp-vocab
Diff against target: 526 lines (+164/-86)
7 files modified
lib/lp/app/widgets/popup.py (+85/-1)
lib/lp/bugs/browser/bugtracker.py (+2/-2)
lib/lp/bugs/browser/widgets/bugtask.py (+11/-74)
lib/lp/registry/browser/productseries.py (+33/-4)
lib/lp/registry/browser/tests/test_packaging.py (+20/-3)
lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py (+11/-0)
lib/lp/registry/vocabularies.py (+2/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/productseries-ubuntupkg-dsp-vocab
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+305367@code.launchpad.net

Commit message

Convert ProductSeries:+ubuntupkg to use the DistributionSourcePackage picker if the appropriate feature flag is set.

Description of the change

Convert ProductSeries:+ubuntupkg to use the DistributionSourcePackage picker if the appropriate feature flag is set.

I had to move most of BugTaskSourcePackageNameWidget into somewhat more common code to achieve this, since this view just asks for the source package name within Ubuntu rather than anything more generic. This will also be useful for the remaining views that need similar work after this (POTemplate:+edit, POTemplate:+admin, and TranslationImportQueueEntry:+index).

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/app/widgets/popup.py'
--- lib/lp/app/widgets/popup.py 2016-07-21 20:22:00 +0000
+++ lib/lp/app/widgets/popup.py 2016-09-09 17:05:20 +0000
@@ -4,21 +4,38 @@
4"""Single selection widget using a popup to select one item from many."""4"""Single selection widget using a popup to select one item from many."""
55
6__metaclass__ = type6__metaclass__ = type
7__all__ = [
8 "BugTrackerPickerWidget",
9 "DistributionSourcePackagePickerWidget",
10 "PersonPickerWidget",
11 "SearchForUpstreamPopupWidget",
12 "SourcePackageNameWidgetBase",
13 "UbuntuSourcePackageNameWidget",
14 "VocabularyPickerWidget",
15 ]
716
8from lazr.restful.utils import safe_hasattr17from lazr.restful.utils import safe_hasattr
9import simplejson18import simplejson
10from z3c.ptcompat import ViewPageTemplateFile19from z3c.ptcompat import ViewPageTemplateFile
20from zope.component import getUtility
21from zope.formlib.interfaces import ConversionError
11from zope.formlib.itemswidgets import (22from zope.formlib.itemswidgets import (
12 ItemsWidgetBase,23 ItemsWidgetBase,
13 SingleDataHelper,24 SingleDataHelper,
14 )25 )
15from zope.schema.interfaces import IChoice26from zope.schema.interfaces import (
27 IChoice,
28 InvalidValue,
29 )
1630
17from lp.app.browser.stringformatter import FormattersAPI31from lp.app.browser.stringformatter import FormattersAPI
18from lp.app.browser.vocabulary import (32from lp.app.browser.vocabulary import (
19 get_person_picker_entry_metadata,33 get_person_picker_entry_metadata,
20 vocabulary_filters,34 vocabulary_filters,
21 )35 )
36from lp.app.errors import NotFoundError
37from lp.app.interfaces.launchpad import ILaunchpadCelebrities
38from lp.services.features import getFeatureFlag
22from lp.services.propertycache import cachedproperty39from lp.services.propertycache import cachedproperty
23from lp.services.webapp import canonical_url40from lp.services.webapp import canonical_url
24from lp.services.webapp.escaping import structured41from lp.services.webapp.escaping import structured
@@ -288,3 +305,70 @@
288 return self._prefix + 'distribution'305 return self._prefix + 'distribution'
289306
290 distribution_name = ''307 distribution_name = ''
308
309
310class SourcePackageNameWidgetBase(DistributionSourcePackagePickerWidget):
311 """A base widget for choosing a SourcePackageName.
312
313 It accepts both binary and source package names. Widgets inheriting
314 from this must implement `getDistribution`.
315
316 Most views should use LaunchpadTargetWidget or
317 DistributionSourcePackagePickerWidget instead, but this is useful in
318 cases where the distribution is fixed in some other way.
319 """
320
321 # Pages that use this widget don't display the distribution.
322 distribution_id = ''
323
324 def __init__(self, field, vocabulary, request):
325 super(SourcePackageNameWidgetBase, self).__init__(
326 field, vocabulary, request)
327 self.cached_values = {}
328
329 def getDistribution(self):
330 """Get the distribution used for package validation.
331
332 The package name has to be published in the returned distribution.
333 """
334 raise NotImplementedError
335
336 def _toFieldValue(self, input):
337 if not input:
338 return self.context.missing_value
339
340 distribution = self.getDistribution()
341 cached_value = self.cached_values.get(input)
342 if cached_value:
343 return cached_value
344 if bool(getFeatureFlag('disclosure.dsp_picker.enabled')):
345 try:
346 self.context.vocabulary.setDistribution(distribution)
347 return self.context.vocabulary.getTermByToken(input).value
348 except LookupError:
349 raise ConversionError(
350 "Launchpad doesn't know of any source package named"
351 " '%s' in %s." % (input, distribution.displayname))
352 # Else the untrusted SPN vocab was used so it needs secondary
353 # verification.
354 try:
355 source = distribution.guessPublishedSourcePackageName(input)
356 except NotFoundError:
357 try:
358 source = self.convertTokensToValues([input])[0]
359 except InvalidValue:
360 raise ConversionError(
361 "Launchpad doesn't know of any source package named"
362 " '%s' in %s." % (input, distribution.displayname))
363 self.cached_values[input] = source
364 return source
365
366
367class UbuntuSourcePackageNameWidget(SourcePackageNameWidgetBase):
368 """A widget to select Ubuntu packages."""
369
370 distribution_name = 'ubuntu'
371
372 def getDistribution(self):
373 """See `SourcePackageNameWidgetBase`"""
374 return getUtility(ILaunchpadCelebrities).ubuntu
291375
=== modified file 'lib/lp/bugs/browser/bugtracker.py'
--- lib/lp/bugs/browser/bugtracker.py 2015-09-28 17:38:45 +0000
+++ lib/lp/bugs/browser/bugtracker.py 2016-09-09 17:05:20 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2013 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Bug tracker views."""4"""Bug tracker views."""
@@ -40,8 +40,8 @@
40from lp.app.interfaces.launchpad import ILaunchpadCelebrities40from lp.app.interfaces.launchpad import ILaunchpadCelebrities
41from lp.app.validators import LaunchpadValidationError41from lp.app.validators import LaunchpadValidationError
42from lp.app.widgets.itemswidgets import LaunchpadRadioWidget42from lp.app.widgets.itemswidgets import LaunchpadRadioWidget
43from lp.app.widgets.popup import UbuntuSourcePackageNameWidget
43from lp.app.widgets.textwidgets import DelimitedListWidget44from lp.app.widgets.textwidgets import DelimitedListWidget
44from lp.bugs.browser.widgets.bugtask import UbuntuSourcePackageNameWidget
45from lp.bugs.interfaces.bugtracker import (45from lp.bugs.interfaces.bugtracker import (
46 BugTrackerType,46 BugTrackerType,
47 IBugTracker,47 IBugTracker,
4848
=== modified file 'lib/lp/bugs/browser/widgets/bugtask.py'
--- lib/lp/bugs/browser/widgets/bugtask.py 2016-09-09 17:05:19 +0000
+++ lib/lp/bugs/browser/widgets/bugtask.py 2016-09-09 17:05:20 +0000
@@ -15,13 +15,11 @@
15 "DBItemDisplayWidget",15 "DBItemDisplayWidget",
16 "FileBugSourcePackageNameWidget",16 "FileBugSourcePackageNameWidget",
17 "NewLineToSpacesWidget",17 "NewLineToSpacesWidget",
18 "UbuntuSourcePackageNameWidget",
19 ]18 ]
2019
21from z3c.ptcompat import ViewPageTemplateFile20from z3c.ptcompat import ViewPageTemplateFile
22from zope.component import getUtility21from zope.component import getUtility
23from zope.formlib.interfaces import (22from zope.formlib.interfaces import (
24 ConversionError,
25 IDisplayWidget,23 IDisplayWidget,
26 IInputWidget,24 IInputWidget,
27 InputErrors,25 InputErrors,
@@ -39,24 +37,17 @@
39 implementer,37 implementer,
40 Interface,38 Interface,
41 )39 )
42from zope.schema.interfaces import (40from zope.schema.interfaces import ValidationError
43 InvalidValue,
44 ValidationError,
45 )
46from zope.schema.vocabulary import getVocabularyRegistry41from zope.schema.vocabulary import getVocabularyRegistry
4742
48from lp import _43from lp import _
49from lp.app.browser.tales import TeamFormatterAPI44from lp.app.browser.tales import TeamFormatterAPI
50from lp.app.errors import (45from lp.app.errors import UnexpectedFormData
51 NotFoundError,
52 UnexpectedFormData,
53 )
54from lp.app.interfaces.launchpad import ILaunchpadCelebrities
55from lp.app.widgets.helpers import get_widget_template46from lp.app.widgets.helpers import get_widget_template
56from lp.app.widgets.launchpadtarget import LaunchpadTargetWidget47from lp.app.widgets.launchpadtarget import LaunchpadTargetWidget
57from lp.app.widgets.popup import (48from lp.app.widgets.popup import (
58 DistributionSourcePackagePickerWidget,
59 PersonPickerWidget,49 PersonPickerWidget,
50 SourcePackageNameWidgetBase,
60 )51 )
61from lp.app.widgets.textwidgets import (52from lp.app.widgets.textwidgets import (
62 StrippedTextWidget,53 StrippedTextWidget,
@@ -483,27 +474,14 @@
483 return vocabulary474 return vocabulary
484475
485476
486class BugTaskSourcePackageNameWidget(DistributionSourcePackagePickerWidget):477class BugTaskSourcePackageNameWidget(SourcePackageNameWidgetBase):
487 """A widget for associating a bugtask with a SourcePackageName.478 """A widget for associating a bugtask with a SourcePackageName.
488479
489 It accepts both binary and source package names.480 It accepts both binary and source package names.
490 """481 """
491482
492 # Pages that use this widget don't display the distribution, but this
493 # can only be used by bugtasks on the distribution in question so the
494 # vocabulary will be able to work it out for itself.
495 distribution_id = ''
496
497 def __init__(self, field, vocabulary, request):
498 super(BugTaskSourcePackageNameWidget, self).__init__(
499 field, vocabulary, request)
500 self.cached_values = {}
501
502 def getDistribution(self):483 def getDistribution(self):
503 """Get the distribution used for package validation.484 """See `SourcePackageNameWidgetBase`."""
504
505 The package name has to be published in the returned distribution.
506 """
507 field = self.context485 field = self.context
508 distribution = field.context.distribution486 distribution = field.context.distribution
509 if distribution is None and field.context.distroseries is not None:487 if distribution is None and field.context.distroseries is not None:
@@ -513,39 +491,8 @@
513 " bugtasks on distributions or on distribution series.")491 " bugtasks on distributions or on distribution series.")
514 return distribution492 return distribution
515493
516 def _toFieldValue(self, input):494
517 if not input:495class BugTaskAlsoAffectsSourcePackageNameWidget(SourcePackageNameWidgetBase):
518 return self.context.missing_value
519
520 distribution = self.getDistribution()
521 cached_value = self.cached_values.get(input)
522 if cached_value:
523 return cached_value
524 if bool(getFeatureFlag('disclosure.dsp_picker.enabled')):
525 try:
526 self.context.vocabulary.setDistribution(distribution)
527 return self.context.vocabulary.getTermByToken(input).value
528 except LookupError:
529 raise ConversionError(
530 "Launchpad doesn't know of any source package named"
531 " '%s' in %s." % (input, distribution.displayname))
532 # Else the untrusted SPN vocab was used so it needs secondary
533 # verification.
534 try:
535 source = distribution.guessPublishedSourcePackageName(input)
536 except NotFoundError:
537 try:
538 source = self.convertTokensToValues([input])[0]
539 except InvalidValue:
540 raise ConversionError(
541 "Launchpad doesn't know of any source package named"
542 " '%s' in %s." % (input, distribution.displayname))
543 self.cached_values[input] = source
544 return source
545
546
547class BugTaskAlsoAffectsSourcePackageNameWidget(
548 BugTaskSourcePackageNameWidget):
549 """Package widget for +distrotask.496 """Package widget for +distrotask.
550497
551 This widget works the same as `BugTaskSourcePackageNameWidget`, except498 This widget works the same as `BugTaskSourcePackageNameWidget`, except
@@ -555,7 +502,7 @@
555 distribution_id = 'field.distribution'502 distribution_id = 'field.distribution'
556503
557 def getDistribution(self):504 def getDistribution(self):
558 """See `BugTaskSourcePackageNameWidget`."""505 """See `SourcePackageNameWidgetBase`."""
559 distribution_name = self.request.form.get('field.distribution')506 distribution_name = self.request.form.get('field.distribution')
560 if distribution_name is None:507 if distribution_name is None:
561 raise UnexpectedFormData(508 raise UnexpectedFormData(
@@ -568,7 +515,7 @@
568 return distribution515 return distribution
569516
570517
571class FileBugSourcePackageNameWidget(BugTaskSourcePackageNameWidget):518class FileBugSourcePackageNameWidget(SourcePackageNameWidgetBase):
572 """Package widget for +filebug.519 """Package widget for +filebug.
573520
574 This widget works the same as `BugTaskSourcePackageNameWidget`, except521 This widget works the same as `BugTaskSourcePackageNameWidget`, except
@@ -577,7 +524,7 @@
577 """524 """
578525
579 def getDistribution(self):526 def getDistribution(self):
580 """See `BugTaskSourcePackageNameWidget`."""527 """See `SourcePackageNameWidgetBase`."""
581 field = self.context528 field = self.context
582 pillar = field.context.pillar529 pillar = field.context.pillar
583 assert IDistribution.providedBy(pillar), (530 assert IDistribution.providedBy(pillar), (
@@ -586,7 +533,7 @@
586 return pillar533 return pillar
587534
588 def _toFieldValue(self, input):535 def _toFieldValue(self, input):
589 """See `BugTaskSourcePackageNameWidget`."""536 """See `SourcePackageNameWidgetBase`."""
590 source = super(FileBugSourcePackageNameWidget, self)._toFieldValue(537 source = super(FileBugSourcePackageNameWidget, self)._toFieldValue(
591 input)538 input)
592 if (source is not None and539 if (source is not None and
@@ -604,16 +551,6 @@
604 return source551 return source
605552
606553
607class UbuntuSourcePackageNameWidget(BugTaskSourcePackageNameWidget):
608 """A widget to select Ubuntu packages."""
609
610 distribution_name = 'ubuntu'
611
612 def getDistribution(self):
613 """See `BugTaskSourcePackageNameWidget`"""
614 return getUtility(ILaunchpadCelebrities).ubuntu
615
616
617@implementer(IDisplayWidget)554@implementer(IDisplayWidget)
618class AssigneeDisplayWidget(BrowserWidget):555class AssigneeDisplayWidget(BrowserWidget):
619 """A widget for displaying an assignee."""556 """A widget for displaying an assignee."""
620557
=== modified file 'lib/lp/registry/browser/productseries.py'
--- lib/lp/registry/browser/productseries.py 2015-09-29 00:05:21 +0000
+++ lib/lp/registry/browser/productseries.py 2016-09-09 17:05:20 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the1# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""View classes for `IProductSeries`."""4"""View classes for `IProductSeries`."""
@@ -28,6 +28,7 @@
2828
29from operator import attrgetter29from operator import attrgetter
3030
31from lazr.restful.interface import copy_field
31from z3c.ptcompat import ViewPageTemplateFile32from z3c.ptcompat import ViewPageTemplateFile
32from zope.component import getUtility33from zope.component import getUtility
33from zope.formlib import form34from zope.formlib import form
@@ -57,6 +58,7 @@
57from lp.app.enums import ServiceUsage58from lp.app.enums import ServiceUsage
58from lp.app.errors import NotFoundError59from lp.app.errors import NotFoundError
59from lp.app.interfaces.launchpad import ILaunchpadCelebrities60from lp.app.interfaces.launchpad import ILaunchpadCelebrities
61from lp.app.widgets.popup import UbuntuSourcePackageNameWidget
60from lp.app.widgets.textwidgets import StrippedTextWidget62from lp.app.widgets.textwidgets import StrippedTextWidget
61from lp.blueprints.browser.specificationtarget import (63from lp.blueprints.browser.specificationtarget import (
62 HasSpecificationsMenuMixin,64 HasSpecificationsMenuMixin,
@@ -86,6 +88,9 @@
86from lp.registry.browser.product import ProductSetBranchView88from lp.registry.browser.product import ProductSetBranchView
87from lp.registry.enums import VCSType89from lp.registry.enums import VCSType
88from lp.registry.errors import CannotPackageProprietaryProduct90from lp.registry.errors import CannotPackageProprietaryProduct
91from lp.registry.interfaces.distributionsourcepackage import (
92 IDistributionSourcePackage,
93 )
89from lp.registry.interfaces.packaging import (94from lp.registry.interfaces.packaging import (
90 IPackaging,95 IPackaging,
91 IPackagingUtil,96 IPackagingUtil,
@@ -93,6 +98,7 @@
93from lp.registry.interfaces.productseries import IProductSeries98from lp.registry.interfaces.productseries import IProductSeries
94from lp.registry.interfaces.series import SeriesStatus99from lp.registry.interfaces.series import SeriesStatus
95from lp.services.config import config100from lp.services.config import config
101from lp.services.features import getFeatureFlag
96from lp.services.propertycache import cachedproperty102from lp.services.propertycache import cachedproperty
97from lp.services.webapp import (103from lp.services.webapp import (
98 ApplicationMenu,104 ApplicationMenu,
@@ -482,10 +488,25 @@
482 return list(self.context.releases[:12])488 return list(self.context.releases[:12])
483489
484490
491class IPackagingForm(IPackaging):
492
493 sourcepackagename = copy_field(
494 IPackaging['sourcepackagename'],
495 vocabularyName='DistributionSourcePackage')
496
497
485class ProductSeriesUbuntuPackagingView(LaunchpadFormView):498class ProductSeriesUbuntuPackagingView(LaunchpadFormView):
486499
487 schema = IPackaging500 @property
501 def schema(self):
502 """See `LaunchpadFormView`."""
503 if bool(getFeatureFlag('disclosure.dsp_picker.enabled')):
504 return IPackagingForm
505 else:
506 return IPackaging
507
488 field_names = ['sourcepackagename', 'distroseries']508 field_names = ['sourcepackagename', 'distroseries']
509 custom_widget('sourcepackagename', UbuntuSourcePackageNameWidget)
489 page_title = 'Ubuntu source packaging'510 page_title = 'Ubuntu source packaging'
490 label = page_title511 label = page_title
491512
@@ -497,7 +518,11 @@
497 self._ubuntu_series = self._ubuntu.currentseries518 self._ubuntu_series = self._ubuntu.currentseries
498 try:519 try:
499 package = self.context.getPackage(self._ubuntu_series)520 package = self.context.getPackage(self._ubuntu_series)
500 self.default_sourcepackagename = package.sourcepackagename521 if bool(getFeatureFlag('disclosure.dsp_picker.enabled')):
522 self.default_sourcepackagename = self._ubuntu.getSourcePackage(
523 package.sourcepackagename)
524 else:
525 self.default_sourcepackagename = package.sourcepackagename
501 except NotFoundError:526 except NotFoundError:
502 # The package has never been set.527 # The package has never been set.
503 self.default_sourcepackagename = None528 self.default_sourcepackagename = None
@@ -555,6 +580,8 @@
555 def validate(self, data):580 def validate(self, data):
556 productseries = self.context581 productseries = self.context
557 sourcepackagename = data.get('sourcepackagename', None)582 sourcepackagename = data.get('sourcepackagename', None)
583 if IDistributionSourcePackage.providedBy(sourcepackagename):
584 sourcepackagename = sourcepackagename.sourcepackagename
558 distroseries = self._getSubmittedSeries(data)585 distroseries = self._getSubmittedSeries(data)
559586
560 packaging_util = getUtility(IPackagingUtil)587 packaging_util = getUtility(IPackagingUtil)
@@ -595,6 +622,8 @@
595 # ubuntu series. if none exists, one will be created622 # ubuntu series. if none exists, one will be created
596 distroseries = self._getSubmittedSeries(data)623 distroseries = self._getSubmittedSeries(data)
597 sourcepackagename = data['sourcepackagename']624 sourcepackagename = data['sourcepackagename']
625 if IDistributionSourcePackage.providedBy(sourcepackagename):
626 sourcepackagename = sourcepackagename.sourcepackagename
598 if getUtility(IPackagingUtil).packagingEntryExists(627 if getUtility(IPackagingUtil).packagingEntryExists(
599 sourcepackagename, distroseries, productseries=self.context):628 sourcepackagename, distroseries, productseries=self.context):
600 # There is no change.629 # There is no change.
@@ -602,7 +631,7 @@
602 try:631 try:
603 self.context.setPackaging(632 self.context.setPackaging(
604 distroseries, sourcepackagename, self.user)633 distroseries, sourcepackagename, self.user)
605 except CannotPackageProprietaryProduct, e:634 except CannotPackageProprietaryProduct as e:
606 self.request.response.addErrorNotification(str(e))635 self.request.response.addErrorNotification(str(e))
607636
608637
609638
=== modified file 'lib/lp/registry/browser/tests/test_packaging.py'
--- lib/lp/registry/browser/tests/test_packaging.py 2016-06-17 15:46:57 +0000
+++ lib/lp/registry/browser/tests/test_packaging.py 2016-09-09 17:05:20 +0000
@@ -5,6 +5,10 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8from testscenarios import (
9 load_tests_apply_scenarios,
10 WithScenarios,
11 )
8from zope.component import getUtility12from zope.component import getUtility
913
10from lp.app.interfaces.launchpad import ILaunchpadCelebrities14from lp.app.interfaces.launchpad import ILaunchpadCelebrities
@@ -15,6 +19,7 @@
15 )19 )
16from lp.registry.interfaces.product import IProductSet20from lp.registry.interfaces.product import IProductSet
17from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet21from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
22from lp.services.features.testing import FeatureFixture
18from lp.testing import (23from lp.testing import (
19 login,24 login,
20 logout,25 logout,
@@ -28,13 +33,22 @@
28from lp.testing.views import create_initialized_view33from lp.testing.views import create_initialized_view
2934
3035
31class TestProductSeriesUbuntuPackagingView(TestCaseWithFactory):36class TestProductSeriesUbuntuPackagingView(WithScenarios, TestCaseWithFactory):
32 """Browser tests for deletion of Packaging objects."""37 """Browser tests for adding Packaging objects."""
3338
34 layer = DatabaseFunctionalLayer39 layer = DatabaseFunctionalLayer
3540
41 scenarios = [
42 ("spn_picker", {"features": {}}),
43 ("dsp_picker", {
44 "features": {u"disclosure.dsp_picker.enabled": u"on"},
45 }),
46 ]
47
36 def setUp(self):48 def setUp(self):
37 super(TestProductSeriesUbuntuPackagingView, self).setUp()49 super(TestProductSeriesUbuntuPackagingView, self).setUp()
50 if self.features:
51 self.useFixture(FeatureFixture(self.features))
38 self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu52 self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
39 self.hoary = self.ubuntu.getSeries('hoary')53 self.hoary = self.ubuntu.getSeries('hoary')
40 self.sourcepackagename = self.factory.makeSourcePackageName('hot')54 self.sourcepackagename = self.factory.makeSourcePackageName('hot')
@@ -88,7 +102,7 @@
88 'hot</a> package in Hoary is already linked to another series.']102 'hot</a> package in Hoary is already linked to another series.']
89 self.assertEqual(view_errors, view.errors)103 self.assertEqual(view_errors, view.errors)
90104
91 def test_sourcepackgename_required(self):105 def test_sourcepackagename_required(self):
92 # A source package name must be provided.106 # A source package name must be provided.
93 form = {107 form = {
94 'field.distroseries': 'hoary',108 'field.distroseries': 'hoary',
@@ -188,3 +202,6 @@
188 productseries=productseries,202 productseries=productseries,
189 sourcepackagename=package_name,203 sourcepackagename=package_name,
190 distroseries=distroseries))204 distroseries=distroseries))
205
206
207load_tests = load_tests_apply_scenarios
191208
=== modified file 'lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py'
--- lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py 2016-09-09 17:05:19 +0000
+++ lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py 2016-09-09 17:05:20 +0000
@@ -175,6 +175,17 @@
175 self.assertEqual(dsp.name, term.title)175 self.assertEqual(dsp.name, term.title)
176 self.assertEqual(dsp, term.value)176 self.assertEqual(dsp, term.value)
177177
178 def test_toTerm_dsp_no_distribution(self):
179 # The vocabulary can convert a DSP to a term even if it does not yet
180 # have a distribution.
181 dsp = self.factory.makeDistributionSourcePackage(
182 sourcepackagename='foo', with_db=False)
183 vocabulary = DistributionSourcePackageVocabulary(None)
184 term = vocabulary.toTerm(dsp)
185 self.assertEqual(dsp.name, term.token)
186 self.assertEqual(dsp.name, term.title)
187 self.assertEqual(dsp, term.value)
188
178 def test_getTermByToken_error(self):189 def test_getTermByToken_error(self):
179 # An error is raised if the token does not match a official DSP.190 # An error is raised if the token does not match a official DSP.
180 distro = self.factory.makeDistribution()191 distro = self.factory.makeDistribution()
181192
=== modified file 'lib/lp/registry/vocabularies.py'
--- lib/lp/registry/vocabularies.py 2016-09-09 17:05:19 +0000
+++ lib/lp/registry/vocabularies.py 2016-09-09 17:05:20 +0000
@@ -2073,7 +2073,6 @@
20732073
2074 def toTerm(self, spn_or_dsp):2074 def toTerm(self, spn_or_dsp):
2075 """See `IVocabulary`."""2075 """See `IVocabulary`."""
2076 self._assertHasDistribution()
2077 dsp = None2076 dsp = None
2078 binary_names = None2077 binary_names = None
2079 if isinstance(spn_or_dsp, tuple):2078 if isinstance(spn_or_dsp, tuple):
@@ -2088,9 +2087,10 @@
2088 if IDistributionSourcePackage.providedBy(spn_or_dsp):2087 if IDistributionSourcePackage.providedBy(spn_or_dsp):
2089 dsp = spn_or_dsp2088 dsp = spn_or_dsp
2090 elif spn_or_dsp is not None:2089 elif spn_or_dsp is not None:
2090 self._assertHasDistribution()
2091 dsp = self.distribution.getSourcePackage(spn_or_dsp)2091 dsp = self.distribution.getSourcePackage(spn_or_dsp)
2092 if dsp is not None:2092 if dsp is not None:
2093 if dsp == self.dsp or dsp.is_official:2093 if dsp == self.dsp or dsp.is_official or self.distribution is None:
2094 if binary_names:2094 if binary_names:
2095 # Search already did the hard work of looking up binary2095 # Search already did the hard work of looking up binary
2096 # names.2096 # names.