Merge lp:~abentley/launchpad/multiple-series-model into lp:launchpad/db-devel

Proposed by Aaron Bentley on 2010-03-12
Status: Merged
Approved by: Graham Binns on 2010-03-16
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~abentley/launchpad/multiple-series-model
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~abentley/launchpad/multiple-series-recipe
Diff against target: 334 lines (+102/-28)
8 files modified
lib/lp/code/configure.zcml (+1/-1)
lib/lp/code/interfaces/sourcepackagerecipe.py (+15/-6)
lib/lp/code/model/sourcepackagerecipe.py (+22/-7)
lib/lp/code/model/sourcepackagerecipedata.py (+3/-1)
lib/lp/code/model/tests/test_sourcepackagerecipe.py (+40/-0)
lib/lp/code/tests/test_sourcepackagerecipe.py (+16/-11)
lib/lp/services/database/configure.zcml (+3/-1)
lib/lp/testing/factory.py (+2/-1)
To merge this branch: bzr merge lp:~abentley/launchpad/multiple-series-model
Reviewer Review Type Date Requested Status
Graham Binns (community) code 2010-03-12 Approve on 2010-03-16
Review via email: mp+21260@code.launchpad.net

Description of the Change

= Summary =
Provide ORM access to the DB entries defined in the prerequisite branch.

== Proposed fix ==
Add a boolean for build_daily. Add a ReferenceSet for distroseries.

== Pre-implementation notes ==
Preimplementation was with thumper.

== Implementation details ==
It appears no one has used ReferenceSet before, so I had to provide permission
for basic set operations: add, remove, clear.

== Tests ==
bin/test -v test_sourcepackagerecipe

== Demo and Q/A ==
None

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/code/configure.zcml
  database/sampledata/current.sql
  database/sampledata/current-dev.sql
  lib/lp/code/model/sourcepackagerecipedata.py
  .bzrignore
  database/schema/patch-2207-90-0.sql
  database/schema/comments.sql
  database/schema/security.cfg
  lib/lp/code/tests/test_sourcepackagerecipe.py
  lib/lp/testing/factory.py
  lib/lp/code/model/tests/test_sourcepackagerecipe.py
  lib/lp/services/database/configure.zcml
  lib/lp/code/model/sourcepackagerecipe.py
  lib/lp/code/interfaces/sourcepackagerecipe.py

== Pylint notices ==

lib/lp/code/model/sourcepackagerecipedata.py
    14: [F0401] Unable to import 'bzrlib.plugins.builder.recipe' (No module named builder)
    17: [F0401] Unable to import 'lazr.enum' (No module named enum)

lib/lp/code/tests/test_sourcepackagerecipe.py
    11: [F0401] Unable to import 'bzrlib.plugins.builder.recipe' (No module named builder)

lib/lp/testing/factory.py
    1706: [F0401, LaunchpadObjectFactory.makeRecipe] Unable to import 'bzrlib.plugins.builder.recipe' (No module named builder)

lib/lp/code/interfaces/sourcepackagerecipe.py
    21: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)

To post a comment you must log in.
Graham Binns (gmb) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/configure.zcml'
2--- lib/lp/code/configure.zcml 2010-03-05 20:00:56 +0000
3+++ lib/lp/code/configure.zcml 2010-03-12 16:53:19 +0000
4@@ -1010,7 +1010,7 @@
5 <allow interface="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipe"/>
6 <require
7 permission="launchpad.Edit"
8- set_attributes="builder_recipe"/>
9+ set_attributes="builder_recipe build_daily"/>
10 </class>
11 <class
12 class="bzrlib.plugins.builder.recipe.BaseRecipeBranch">
13
14=== modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py'
15--- lib/lp/code/interfaces/sourcepackagerecipe.py 2010-02-05 14:22:03 +0000
16+++ lib/lp/code/interfaces/sourcepackagerecipe.py 2010-03-12 16:53:19 +0000
17@@ -1,9 +1,15 @@
18 # Copyright 2009 Canonical Ltd. This software is licensed under the
19 # GNU Affero General Public License version 3 (see the file LICENSE).
20
21+# pylint: disable-msg=E0211,E0213
22+
23+
24 """Interface of the `SourcePackageRecipe` content type."""
25
26+
27 __metaclass__ = type
28+
29+
30 __all__ = [
31 'ForbiddenInstruction',
32 'ISourcePackageRecipe',
33@@ -11,10 +17,11 @@
34 'TooNewRecipeFormat',
35 ]
36
37-from lazr.restful.fields import Reference
38+
39+from lazr.restful.fields import CollectionField, Reference
40
41 from zope.interface import Attribute, Interface
42-from zope.schema import Datetime, TextLine
43+from zope.schema import Bool, Datetime, TextLine
44
45 from canonical.launchpad import _
46 from canonical.launchpad.validators.name import name_validator
47@@ -57,10 +64,12 @@
48 owner = Reference(
49 IPerson, title=_("The person or team who can edit this recipe"),
50 readonly=False)
51- distroseries = Reference(
52- IDistroSeries, title=_("The distroseries this recipe will build a "
53- "source package for"),
54- readonly=True)
55+ distroseries = CollectionField(
56+ Reference(IDistroSeries), title=_("The distroseries this recipe will"
57+ " build a source package for"),
58+ readonly=False)
59+ build_daily = Bool(
60+ title=_("If true, the recipe should be built daily."))
61 sourcepackagename = Reference(
62 ISourcePackageName, title=_("The name of the source package this "
63 "recipe will build a source package"),
64
65=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
66--- lib/lp/code/model/sourcepackagerecipe.py 2010-02-05 15:38:19 +0000
67+++ lib/lp/code/model/sourcepackagerecipe.py 2010-03-12 16:53:19 +0000
68@@ -8,7 +8,8 @@
69 'SourcePackageRecipe',
70 ]
71
72-from storm.locals import Int, Reference, Store, Storm, Unicode
73+from storm.locals import (
74+ Bool, Int, Reference, ReferenceSet, Store, Storm, Unicode)
75
76 from zope.component import getUtility
77 from zope.interface import classProvides, implements
78@@ -22,6 +23,7 @@
79 from lp.code.interfaces.sourcepackagerecipebuild import (
80 ISourcePackageRecipeBuildSource)
81 from lp.code.model.sourcepackagerecipedata import _SourcePackageRecipeData
82+from lp.registry.model.distroseries import DistroSeries
83 from lp.soyuz.interfaces.archive import ArchivePurpose
84 from lp.soyuz.interfaces.component import IComponentSet
85
86@@ -31,6 +33,15 @@
87 unsupported."""
88
89
90+class _SourcePackageRecipeDistroSeries(Storm):
91+ """Link table for many-to-many relationship."""
92+
93+ __storm_table__ = "SourcePackageRecipeDistroSeries"
94+ id = Int(primary=True)
95+ sourcepackagerecipe_id = Int(name='sourcepackagerecipe', allow_none=False)
96+ distroseries_id = Int(name='distroseries', allow_none=False)
97+
98+
99 class SourcePackageRecipe(Storm):
100 """See `ISourcePackageRecipe` and `ISourcePackageRecipeSource`."""
101
102@@ -50,8 +61,11 @@
103 registrant_id = Int(name='registrant', allow_none=True)
104 registrant = Reference(registrant_id, 'Person.id')
105
106- distroseries_id = Int(name='distroseries', allow_none=True)
107- distroseries = Reference(distroseries_id, 'DistroSeries.id')
108+ distroseries = ReferenceSet(
109+ id, _SourcePackageRecipeDistroSeries.sourcepackagerecipe_id,
110+ _SourcePackageRecipeDistroSeries.distroseries_id, DistroSeries.id)
111+
112+ build_daily = Bool()
113
114 sourcepackagename_id = Int(name='sourcepackagename', allow_none=True)
115 sourcepackagename = Reference(
116@@ -88,24 +102,25 @@
117 _SourcePackageRecipeData(builder_recipe, sprecipe)
118 sprecipe.registrant = registrant
119 sprecipe.owner = owner
120- sprecipe.distroseries = distroseries
121 sprecipe.sourcepackagename = sourcepackagename
122 sprecipe.name = name
123+ for distroseries_item in distroseries:
124+ sprecipe.distroseries.add(distroseries_item)
125 store.add(sprecipe)
126 return sprecipe
127
128- def requestBuild(self, archive, requester, pocket):
129+ def requestBuild(self, archive, requester, distroseries, pocket):
130 """See `ISourcePackageRecipe`."""
131 if archive.purpose != ArchivePurpose.PPA:
132 raise NonPPABuildRequest
133 component = getUtility(IComponentSet)["multiverse"]
134 reject_reason = check_upload_to_archive(
135- requester, self.distroseries, self.sourcepackagename,
136+ requester, distroseries, self.sourcepackagename,
137 archive, component, pocket)
138 if reject_reason is not None:
139 raise reject_reason
140
141- sourcepackage = self.distroseries.getSourcePackage(
142+ sourcepackage = distroseries.getSourcePackage(
143 self.sourcepackagename)
144 build = getUtility(ISourcePackageRecipeBuildSource).new(sourcepackage,
145 self, requester, archive)
146
147=== modified file 'lib/lp/code/model/sourcepackagerecipedata.py'
148--- lib/lp/code/model/sourcepackagerecipedata.py 2010-02-05 15:07:44 +0000
149+++ lib/lp/code/model/sourcepackagerecipedata.py 2010-03-12 16:53:19 +0000
150@@ -1,4 +1,4 @@
151-# Copyright 2009 Canonical Ltd. This software is licensed under the
152+# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
153 # GNU Affero General Public License version 3 (see the file LICENSE).
154
155 """Implementation of the recipe storage.
156@@ -50,6 +50,7 @@
157
158 def __init__(self, name, type, comment, line_number, branch, revspec,
159 directory, recipe_data, parent_instruction):
160+ super(_SourcePackageRecipeDataInstruction, self).__init__()
161 self.name = unicode(name)
162 self.type = type
163 self.comment = comment
164@@ -215,6 +216,7 @@
165 def __init__(self, recipe, sourcepackage_recipe):
166 """Initialize from the bzr-builder recipe and link it to a db recipe.
167 """
168+ super(_SourcePackageRecipeData, self).__init__()
169 self.setRecipe(recipe)
170 self.sourcepackage_recipe = sourcepackage_recipe
171
172
173=== added file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py'
174--- lib/lp/code/model/tests/test_sourcepackagerecipe.py 1970-01-01 00:00:00 +0000
175+++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2010-03-12 16:53:19 +0000
176@@ -0,0 +1,40 @@
177+# Copyright 2010 Canonical Ltd. This software is licensed under the
178+# GNU Affero General Public License version 3 (see the file LICENSE).
179+
180+
181+__metaclass__ = type
182+
183+
184+import unittest
185+
186+from canonical.testing import DatabaseFunctionalLayer
187+from lp.testing import login_person, TestCaseWithFactory
188+
189+
190+class TestSourcePackageRecipe(TestCaseWithFactory):
191+
192+ layer = DatabaseFunctionalLayer
193+
194+ def test_distroseries(self):
195+ """Test that the distroseries behaves as a set."""
196+ recipe = self.factory.makeSourcePackageRecipe()
197+ distroseries = self.factory.makeDistroSeries()
198+ (old_distroseries,) = recipe.distroseries
199+ recipe.distroseries.add(distroseries)
200+ self.assertEqual(
201+ set([distroseries, old_distroseries]), set(recipe.distroseries))
202+ recipe.distroseries.remove(distroseries)
203+ self.assertEqual([old_distroseries], list(recipe.distroseries))
204+ recipe.distroseries.clear()
205+ self.assertEqual([], list(recipe.distroseries))
206+
207+ def test_build_daily(self):
208+ """Test that build_daily behaves as a bool."""
209+ recipe = self.factory.makeSourcePackageRecipe()
210+ self.assertFalse(recipe.build_daily)
211+ login_person(recipe.owner)
212+ recipe.build_daily = True
213+ self.assertTrue(recipe.build_daily)
214+
215+def test_suite():
216+ return unittest.TestLoader().loadTestsFromName(__name__)
217
218=== modified file 'lib/lp/code/tests/test_sourcepackagerecipe.py'
219--- lib/lp/code/tests/test_sourcepackagerecipe.py 2010-03-05 13:52:32 +0000
220+++ lib/lp/code/tests/test_sourcepackagerecipe.py 2010-03-12 16:53:19 +0000
221@@ -52,7 +52,7 @@
222 sourcepackagename = self.factory.makeSourcePackageName()
223 name = self.factory.getUniqueString(u'recipe-name')
224 return getUtility(ISourcePackageRecipeSource).new(
225- registrant=registrant, owner=owner, distroseries=distroseries,
226+ registrant=registrant, owner=owner, distroseries=[distroseries],
227 sourcepackagename=sourcepackagename, name=name,
228 builder_recipe=builder_recipe)
229
230@@ -66,12 +66,12 @@
231 name = self.factory.getUniqueString(u'recipe-name')
232 builder_recipe = self.factory.makeRecipe()
233 recipe = getUtility(ISourcePackageRecipeSource).new(
234- registrant=registrant, owner=owner, distroseries=distroseries,
235+ registrant=registrant, owner=owner, distroseries=[distroseries],
236 sourcepackagename=sourcepackagename, name=name,
237 builder_recipe=builder_recipe)
238 self.assertEquals(
239- (registrant, owner, distroseries, sourcepackagename, name),
240- (recipe.registrant, recipe.owner, recipe.distroseries,
241+ (registrant, owner, set([distroseries]), sourcepackagename, name),
242+ (recipe.registrant, recipe.owner, set(recipe.distroseries),
243 recipe.sourcepackagename, recipe.name))
244
245 def test_source_implements_interface(self):
246@@ -173,12 +173,13 @@
247
248 def test_requestBuild(self):
249 recipe = self.factory.makeSourcePackageRecipe()
250+ (distroseries,) = list(recipe.distroseries)
251 ppa = self.factory.makeArchive()
252- build = recipe.requestBuild(ppa, ppa.owner,
253+ build = recipe.requestBuild(ppa, ppa.owner, distroseries,
254 PackagePublishingPocket.RELEASE)
255 self.assertProvides(build, ISourcePackageRecipeBuild)
256 self.assertEqual(build.archive, ppa)
257- self.assertEqual(build.distroseries, recipe.distroseries)
258+ self.assertEqual(build.distroseries, distroseries)
259 self.assertEqual(build.requester, ppa.owner)
260 store = Store.of(build)
261 store.flush()
262@@ -196,28 +197,32 @@
263 def test_requestBuildRejectsNotPPA(self):
264 recipe = self.factory.makeSourcePackageRecipe()
265 not_ppa = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
266+ (distroseries,) = list(recipe.distroseries)
267 self.assertRaises(NonPPABuildRequest, recipe.requestBuild, not_ppa,
268- not_ppa.owner, PackagePublishingPocket.RELEASE)
269+ not_ppa.owner, distroseries, PackagePublishingPocket.RELEASE)
270
271 def test_requestBuildRejectsNoPermission(self):
272 recipe = self.factory.makeSourcePackageRecipe()
273 ppa = self.factory.makeArchive()
274 requester = self.factory.makePerson()
275+ (distroseries,) = list(recipe.distroseries)
276 self.assertRaises(CannotUploadToArchive, recipe.requestBuild, ppa,
277- requester, PackagePublishingPocket.RELEASE)
278+ requester, distroseries, PackagePublishingPocket.RELEASE)
279
280 def test_requestBuildRejectsInvalidPocket(self):
281 recipe = self.factory.makeSourcePackageRecipe()
282 ppa = self.factory.makeArchive()
283+ (distroseries,) = list(recipe.distroseries)
284 self.assertRaises(InvalidPocketForPPA, recipe.requestBuild, ppa,
285- ppa.owner, PackagePublishingPocket.BACKPORTS)
286+ ppa.owner, distroseries, PackagePublishingPocket.BACKPORTS)
287
288 def test_requestBuildRejectsDisabledArchive(self):
289 recipe = self.factory.makeSourcePackageRecipe()
290 ppa = self.factory.makeArchive()
291 removeSecurityProxy(ppa).disable()
292+ (distroseries,) = list(recipe.distroseries)
293 self.assertRaises(ArchiveDisabled, recipe.requestBuild, ppa,
294- ppa.owner, PackagePublishingPocket.RELEASE)
295+ ppa.owner, distroseries, PackagePublishingPocket.RELEASE)
296
297
298 class TestRecipeBranchRoundTripping(TestCaseWithFactory):
299@@ -243,7 +248,7 @@
300 sourcepackagename = self.factory.makeSourcePackageName()
301 name = self.factory.getUniqueString(u'recipe-name')
302 recipe = getUtility(ISourcePackageRecipeSource).new(
303- registrant=registrant, owner=owner, distroseries=distroseries,
304+ registrant=registrant, owner=owner, distroseries=[distroseries],
305 sourcepackagename=sourcepackagename, name=name,
306 builder_recipe=builder_recipe)
307 return recipe.builder_recipe
308
309=== modified file 'lib/lp/services/database/configure.zcml'
310--- lib/lp/services/database/configure.zcml 2009-10-28 11:53:50 +0000
311+++ lib/lp/services/database/configure.zcml 2010-03-12 16:53:19 +0000
312@@ -12,5 +12,7 @@
313 <allow interface="storm.zope.interfaces.IResultSet" />
314 <allow attributes="__getslice__" />
315 </class>
316-
317+ <class class="storm.references.BoundIndirectReferenceSet">
318+ <allow attributes="add remove clear" />
319+ </class>
320 </configure>
321
322=== modified file 'lib/lp/testing/factory.py'
323--- lib/lp/testing/factory.py 2010-03-08 12:35:15 +0000
324+++ lib/lp/testing/factory.py 2010-03-12 16:53:19 +0000
325@@ -1730,7 +1730,8 @@
326 name = self.getUniqueString().decode('utf8')
327 recipe = self.makeRecipe(*branches)
328 return getUtility(ISourcePackageRecipeSource).new(
329- registrant, owner, distroseries, sourcepackagename, name, recipe)
330+ registrant, owner, [distroseries], sourcepackagename, name,
331+ recipe)
332
333 def makeSourcePackageRecipeBuild(self, sourcepackage=None, recipe=None,
334 requester=None, archive=None,

Subscribers

People subscribed via source and target branches

to status/vote changes: