Merge lp:~cjwatson/launchpad/sprd-utilities into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17887
Proposed branch: lp:~cjwatson/launchpad/sprd-utilities
Merge into: lp:launchpad
Diff against target: 302 lines (+78/-32)
6 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+15/-14)
lib/lp/code/configure.zcml (+10/-1)
lib/lp/code/interfaces/sourcepackagerecipe.py (+25/-1)
lib/lp/code/model/sourcepackagerecipe.py (+5/-3)
lib/lp/code/model/sourcepackagerecipebuild.py (+9/-5)
lib/lp/code/model/sourcepackagerecipedata.py (+14/-8)
To merge this branch: bzr merge lp:~cjwatson/launchpad/sprd-utilities
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+282218@code.launchpad.net

Commit message

Access recipe parsing via interfaces.

Description of the change

Access recipe parsing via interfaces.

This is a bit of refactoring before starting on Git recipe support; for that, we're going to want to push recipe text through a small amount of munging before passing it to RecipeParser, so it makes sense for it all to go through common code, and that ought to use an interface so that it can be used correctly from browser code.

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

I don't understand why there are two interfaces and utilities, but otherwise good.

review: Approve (code)
Revision history for this message
Colin Watson (cjwatson) wrote :

Mainly because the method that returns a RecipeBranch can't reasonably be a securedutility (there's no Zope interface declared for that, so returning proxied objects is unhelpful), while the method that returns a SourcePackageRecipeData should be. Also the naming feels a little more natural this way.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2015-07-08 16:05:11 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2016-01-11 19:38:23 +0000
@@ -1,4 +1,4 @@
1# Copyright 2010-2013 Canonical Ltd. This software is licensed under the1# Copyright 2010-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"""SourcePackageRecipe views."""4"""SourcePackageRecipe views."""
@@ -20,7 +20,6 @@
20from bzrlib.plugins.builder.recipe import (20from bzrlib.plugins.builder.recipe import (
21 ForbiddenInstructionError,21 ForbiddenInstructionError,
22 RecipeParseError,22 RecipeParseError,
23 RecipeParser,
24 )23 )
25from lazr.lifecycle.event import ObjectModifiedEvent24from lazr.lifecycle.event import ObjectModifiedEvent
26from lazr.lifecycle.snapshot import Snapshot25from lazr.lifecycle.snapshot import Snapshot
@@ -92,6 +91,7 @@
92 )91 )
93from lp.code.interfaces.branchtarget import IBranchTarget92from lp.code.interfaces.branchtarget import IBranchTarget
94from lp.code.interfaces.sourcepackagerecipe import (93from lp.code.interfaces.sourcepackagerecipe import (
94 IRecipeBranchSource,
95 ISourcePackageRecipe,95 ISourcePackageRecipe,
96 ISourcePackageRecipeSource,96 ISourcePackageRecipeSource,
97 MINIMAL_RECIPE_TEXT,97 MINIMAL_RECIPE_TEXT,
@@ -611,10 +611,11 @@
611 'distroseries',611 'distroseries',
612 'You must specify at least one series for daily builds.')612 'You must specify at least one series for daily builds.')
613 try:613 try:
614 parser = RecipeParser(data['recipe_text'])614 self.error_handler(
615 parser.parse()615 getUtility(IRecipeBranchSource).getParsedRecipe,
616 except RecipeParseError as error:616 data['recipe_text'])
617 self.setFieldError('recipe_text', str(error))617 except ErrorHandled:
618 pass
618619
619 def error_handler(self, callable, *args, **kwargs):620 def error_handler(self, callable, *args, **kwargs):
620 try:621 try:
@@ -631,7 +632,7 @@
631 except NoSuchBranch as e:632 except NoSuchBranch as e:
632 self.setFieldError(633 self.setFieldError(
633 'recipe_text', '%s is not a branch on Launchpad.' % e.name)634 'recipe_text', '%s is not a branch on Launchpad.' % e.name)
634 except PrivateBranchRecipe as e:635 except (RecipeParseError, PrivateBranchRecipe) as e:
635 self.setFieldError('recipe_text', str(e))636 self.setFieldError('recipe_text', str(e))
636 raise ErrorHandled()637 raise ErrorHandled()
637638
@@ -872,14 +873,14 @@
872 self.context, providing=providedBy(self.context))873 self.context, providing=providedBy(self.context))
873874
874 recipe_text = data.pop('recipe_text')875 recipe_text = data.pop('recipe_text')
875 parser = RecipeParser(recipe_text)876 try:
876 recipe = parser.parse()877 recipe = self.error_handler(
877 if self.context.builder_recipe != recipe:878 getUtility(IRecipeBranchSource).getParsedRecipe, recipe_text)
878 try:879 if self.context.builder_recipe != recipe:
879 self.error_handler(self.context.setRecipeText, recipe_text)880 self.error_handler(self.context.setRecipeText, recipe_text)
880 changed = True881 changed = True
881 except ErrorHandled:882 except ErrorHandled:
882 return883 return
883884
884 distros = data.pop('distroseries')885 distros = data.pop('distroseries')
885 if distros != self.context.distroseries:886 if distros != self.context.distroseries:
@@ -927,7 +928,7 @@
927 label = title928 label = title
928929
929 class schema(Interface):930 class schema(Interface):
930 """Schema for deleting a branch."""931 """Schema for deleting a recipe."""
931932
932 @property933 @property
933 def cancel_url(self):934 def cancel_url(self):
934935
=== modified file 'lib/lp/code/configure.zcml'
--- lib/lp/code/configure.zcml 2015-10-09 16:56:45 +0000
+++ lib/lp/code/configure.zcml 2016-01-11 19:38:23 +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).
3-->3-->
44
@@ -1053,6 +1053,15 @@
1053 <require permission="launchpad.View"1053 <require permission="launchpad.View"
1054 interface="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipeData"/>1054 interface="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipeData"/>
1055 </class>1055 </class>
1056 <utility
1057 component="lp.code.model.sourcepackagerecipedata.SourcePackageRecipeData"
1058 provides="lp.code.interfaces.sourcepackagerecipe.IRecipeBranchSource">
1059 </utility>
1060 <securedutility
1061 component="lp.code.model.sourcepackagerecipedata.SourcePackageRecipeData"
1062 provides="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipeDataSource">
1063 <allow interface="lp.code.interfaces.sourcepackagerecipe.ISourcePackageRecipeDataSource"/>
1064 </securedutility>
1056 <!-- SourcePackageRecipe -->1065 <!-- SourcePackageRecipe -->
1057 <class1066 <class
1058 class="lp.code.model.sourcepackagerecipe.SourcePackageRecipe">1067 class="lp.code.model.sourcepackagerecipe.SourcePackageRecipe">
10591068
=== modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py'
--- lib/lp/code/interfaces/sourcepackagerecipe.py 2015-04-30 01:45:30 +0000
+++ lib/lp/code/interfaces/sourcepackagerecipe.py 2016-01-11 19:38:23 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2012 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"""Interface of the `SourcePackageRecipe` content type."""4"""Interface of the `SourcePackageRecipe` content type."""
@@ -8,8 +8,10 @@
88
99
10__all__ = [10__all__ = [
11 'IRecipeBranchSource',
11 'ISourcePackageRecipe',12 'ISourcePackageRecipe',
12 'ISourcePackageRecipeData',13 'ISourcePackageRecipeData',
14 'ISourcePackageRecipeDataSource',
13 'ISourcePackageRecipeSource',15 'ISourcePackageRecipeSource',
14 'MINIMAL_RECIPE_TEXT',16 'MINIMAL_RECIPE_TEXT',
15 ]17 ]
@@ -88,6 +90,28 @@
88 """An iterator of the branches referenced by this recipe."""90 """An iterator of the branches referenced by this recipe."""
8991
9092
93class IRecipeBranchSource(Interface):
94
95 def getParsedRecipe(recipe_text):
96 """Parse recipe text into recipe data.
97
98 :param recipe_text: Recipe text as a string.
99 :return: a `RecipeBranch` representing the recipe.
100 """
101
102
103class ISourcePackageRecipeDataSource(Interface):
104
105 def createManifestFromText(text, sourcepackage_recipe_build):
106 """Create a manifest for the specified build.
107
108 :param text: The text of the recipe to create a manifest for.
109 :param sourcepackage_recipe_build: The build to associate the manifest
110 with.
111 :return: an instance of `SourcePackageRecipeData`.
112 """
113
114
91class ISourcePackageRecipeView(Interface):115class ISourcePackageRecipeView(Interface):
92 """IBranch attributes that require launchpad.View permission."""116 """IBranch attributes that require launchpad.View permission."""
93117
94118
=== modified file 'lib/lp/code/model/sourcepackagerecipe.py'
--- lib/lp/code/model/sourcepackagerecipe.py 2015-09-28 17:38:45 +0000
+++ lib/lp/code/model/sourcepackagerecipe.py 2016-01-11 19:38:23 +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"""Implementation of the `SourcePackageRecipe` content type."""4"""Implementation of the `SourcePackageRecipe` content type."""
@@ -42,6 +42,7 @@
42 BuildNotAllowedForDistro,42 BuildNotAllowedForDistro,
43 )43 )
44from lp.code.interfaces.sourcepackagerecipe import (44from lp.code.interfaces.sourcepackagerecipe import (
45 IRecipeBranchSource,
45 ISourcePackageRecipe,46 ISourcePackageRecipe,
46 ISourcePackageRecipeData,47 ISourcePackageRecipeData,
47 ISourcePackageRecipeSource,48 ISourcePackageRecipeSource,
@@ -167,7 +168,7 @@
167 owner_ids, need_validity=True))168 owner_ids, need_validity=True))
168169
169 def setRecipeText(self, recipe_text):170 def setRecipeText(self, recipe_text):
170 parsed = SourcePackageRecipeData.getParsedRecipe(recipe_text)171 parsed = getUtility(IRecipeBranchSource).getParsedRecipe(recipe_text)
171 self._recipe_data.setRecipe(parsed)172 self._recipe_data.setRecipe(parsed)
172173
173 @property174 @property
@@ -187,7 +188,8 @@
187 """See `ISourcePackageRecipeSource.new`."""188 """See `ISourcePackageRecipeSource.new`."""
188 store = IMasterStore(SourcePackageRecipe)189 store = IMasterStore(SourcePackageRecipe)
189 sprecipe = SourcePackageRecipe()190 sprecipe = SourcePackageRecipe()
190 builder_recipe = SourcePackageRecipeData.getParsedRecipe(recipe)191 builder_recipe = getUtility(IRecipeBranchSource).getParsedRecipe(
192 recipe)
191 SourcePackageRecipeData(builder_recipe, sprecipe)193 SourcePackageRecipeData(builder_recipe, sprecipe)
192 sprecipe.registrant = registrant194 sprecipe.registrant = registrant
193 sprecipe.owner = owner195 sprecipe.owner = owner
194196
=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py 2015-09-11 15:11:34 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py 2016-01-11 19:38:23 +0000
@@ -1,4 +1,4 @@
1# Copyright 2010-2013 Canonical Ltd. This software is licensed under the1# Copyright 2010-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"""Implementation code for source package builds."""4"""Implementation code for source package builds."""
@@ -46,6 +46,10 @@
46 BuildAlreadyPending,46 BuildAlreadyPending,
47 BuildNotAllowedForDistro,47 BuildNotAllowedForDistro,
48 )48 )
49from lp.code.interfaces.sourcepackagerecipe import (
50 IRecipeBranchSource,
51 ISourcePackageRecipeDataSource,
52 )
49from lp.code.interfaces.sourcepackagerecipebuild import (53from lp.code.interfaces.sourcepackagerecipebuild import (
50 ISourcePackageRecipeBuild,54 ISourcePackageRecipeBuild,
51 ISourcePackageRecipeBuildSource,55 ISourcePackageRecipeBuildSource,
@@ -53,7 +57,6 @@
53from lp.code.mail.sourcepackagerecipebuild import (57from lp.code.mail.sourcepackagerecipebuild import (
54 SourcePackageRecipeBuildMailer,58 SourcePackageRecipeBuildMailer,
55 )59 )
56from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData
57from lp.registry.interfaces.pocket import PackagePublishingPocket60from lp.registry.interfaces.pocket import PackagePublishingPocket
58from lp.registry.model.person import Person61from lp.registry.model.person import Person
59from lp.services.database.bulk import load_related62from lp.services.database.bulk import load_related
@@ -158,10 +161,11 @@
158 if self.manifest is not None:161 if self.manifest is not None:
159 IStore(self.manifest).remove(self.manifest)162 IStore(self.manifest).remove(self.manifest)
160 elif self.manifest is None:163 elif self.manifest is None:
161 SourcePackageRecipeData.createManifestFromText(text, self)164 getUtility(ISourcePackageRecipeDataSource).createManifestFromText(
165 text, self)
162 else:166 else:
163 from bzrlib.plugins.builder.recipe import RecipeParser167 self.manifest.setRecipe(
164 self.manifest.setRecipe(RecipeParser(text).parse())168 getUtility(IRecipeBranchSource).getParsedRecipe(text))
165169
166 def getManifestText(self):170 def getManifestText(self):
167 if self.manifest is None:171 if self.manifest is None:
168172
=== modified file 'lib/lp/code/model/sourcepackagerecipedata.py'
--- lib/lp/code/model/sourcepackagerecipedata.py 2015-02-25 11:21:43 +0000
+++ lib/lp/code/model/sourcepackagerecipedata.py 2016-01-11 19:38:23 +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"""Implementation of the recipe storage.4"""Implementation of the recipe storage.
@@ -38,6 +38,10 @@
38 Unicode,38 Unicode,
39 )39 )
40from zope.component import getUtility40from zope.component import getUtility
41from zope.interface import (
42 implementer,
43 provider,
44 )
4145
42from lp.code.errors import (46from lp.code.errors import (
43 NoSuchBranch,47 NoSuchBranch,
@@ -45,6 +49,11 @@
45 TooNewRecipeFormat,49 TooNewRecipeFormat,
46 )50 )
47from lp.code.interfaces.branchlookup import IBranchLookup51from lp.code.interfaces.branchlookup import IBranchLookup
52from lp.code.interfaces.sourcepackagerecipe import (
53 IRecipeBranchSource,
54 ISourcePackageRecipeData,
55 ISourcePackageRecipeDataSource,
56 )
48from lp.code.model.branch import Branch57from lp.code.model.branch import Branch
49from lp.services.database.bulk import (58from lp.services.database.bulk import (
50 load_referencing,59 load_referencing,
@@ -142,6 +151,8 @@
142MAX_RECIPE_FORMAT = 0.4151MAX_RECIPE_FORMAT = 0.4
143152
144153
154@implementer(ISourcePackageRecipeData)
155@provider(IRecipeBranchSource, ISourcePackageRecipeDataSource)
145class SourcePackageRecipeData(Storm):156class SourcePackageRecipeData(Storm):
146 """The database representation of a BaseRecipeBranch from bzr-builder.157 """The database representation of a BaseRecipeBranch from bzr-builder.
147158
@@ -177,6 +188,7 @@
177188
178 @staticmethod189 @staticmethod
179 def getParsedRecipe(recipe_text):190 def getParsedRecipe(recipe_text):
191 """See `IRecipeBranchSource`."""
180 parser = RecipeParser(recipe_text)192 parser = RecipeParser(recipe_text)
181 return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS)193 return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS)
182194
@@ -202,13 +214,7 @@
202214
203 @classmethod215 @classmethod
204 def createManifestFromText(cls, text, sourcepackage_recipe_build):216 def createManifestFromText(cls, text, sourcepackage_recipe_build):
205 """Create a manifest for the specified build.217 """See `ISourcePackageRecipeDataSource`."""
206
207 :param text: The text of the recipe to create a manifest for.
208 :param sourcepackage_recipe_build: The build to associate the manifest
209 with.
210 :return: an instance of SourcePackageRecipeData.
211 """
212 parsed = cls.getParsedRecipe(text)218 parsed = cls.getParsedRecipe(text)
213 return cls(219 return cls(
214 parsed, sourcepackage_recipe_build=sourcepackage_recipe_build)220 parsed, sourcepackage_recipe_build=sourcepackage_recipe_build)