Merge lp:~stevenk/launchpad/set-recipe-text-bad-data into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 12385
Proposed branch: lp:~stevenk/launchpad/set-recipe-text-bad-data
Merge into: lp:launchpad
Diff against target: 141 lines (+51/-45)
2 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+38/-45)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+13/-0)
To merge this branch: bzr merge lp:~stevenk/launchpad/set-recipe-text-bad-data
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+48853@code.launchpad.net

Commit message

[r=lifeless][bug=592513,683321] Don't OOPS if an invalid branch is used while editing a recipe.

Description of the change

Fix bug 683321, 'Editing recipe oopses if invalid branch used'. The real error was that the request_action for creating recipes dealt with four exceptions, but request_action for editing recipes only dealt with three. This means if the branch passed in was invalid, the exception wouldn't be caught and would leak up the stack, causing the OOPS.

I have fixed this by refactoring the exception handler to a parent class of both AddView and EditView, RecipeTextValidatorMixin. While performing this refactoring, I fixed bug 592513 by accident and removed the XXX.

I added a test that checks the error is raised correctly when a recipe is edited and is submitted with a branch that doesn't exist.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

Broadly good; I have some tweaks that may help you.

Firstly, a docstring on the method might be nice, its not clear why you only accept *args, for instance.

Secondly to handle the fact that an error occured, you might like to raise a specific exception instead of returning None.

thusly:
class ErrorHandled(Exception):
    """A field error occured and was handled."""

try:
    self.error_handler(...)
except ErrorHandled:
    return

that, to me, would make it clearer than checking for 'is None'. What do you think?

review: Approve
Revision history for this message
Robert Collins (lifeless) wrote :

To clarify what I mean, the error_handler guts would then be
+ try:
+ return callable(*args)
+ except TooNewRecipeFormat:
....
raise ErrorHandled()

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 2011-02-14 00:58:45 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2011-02-15 11:35:12 +0000
@@ -399,6 +399,10 @@
399 "the owner of the recipe ."))399 "the owner of the recipe ."))
400400
401401
402class ErrorHandled(Exception):
403 """A field error occured and was handled."""
404
405
402class RecipeTextValidatorMixin:406class RecipeTextValidatorMixin:
403 """Class to validate that the Source Package Recipe text is valid."""407 """Class to validate that the Source Package Recipe text is valid."""
404408
@@ -414,6 +418,25 @@
414 except RecipeParseError, error:418 except RecipeParseError, error:
415 self.setFieldError('recipe_text', str(error))419 self.setFieldError('recipe_text', str(error))
416420
421 def error_handler(self, callable, *args, **kwargs):
422 try:
423 return callable(*args)
424 except TooNewRecipeFormat:
425 self.setFieldError(
426 'recipe_text',
427 'The recipe format version specified is not available.')
428 except ForbiddenInstructionError, e:
429 self.setFieldError(
430 'recipe_text',
431 'The bzr-builder instruction "%s" is not permitted '
432 'here.' % e.instruction_name)
433 except NoSuchBranch, e:
434 self.setFieldError(
435 'recipe_text', '%s is not a branch on Launchpad.' % e.name)
436 except PrivateBranchRecipe, e:
437 self.setFieldError('recipe_text', str(e))
438 raise ErrorHandled()
439
417440
418class RelatedBranchesWidget(Widget):441class RelatedBranchesWidget(Widget):
419 """A widget to render the related branches for a recipe."""442 """A widget to render the related branches for a recipe."""
@@ -524,36 +547,20 @@
524547
525 @action('Create Recipe', name='create')548 @action('Create Recipe', name='create')
526 def request_action(self, action, data):549 def request_action(self, action, data):
550 owner = data['owner']
551 if data['use_ppa'] == CREATE_NEW:
552 ppa_name = data.get('ppa_name', None)
553 ppa = owner.createPPA(ppa_name)
554 else:
555 ppa = data['daily_build_archive']
527 try:556 try:
528 owner = data['owner']557 source_package_recipe = self.error_handler(
529 if data['use_ppa'] == CREATE_NEW:558 getUtility(ISourcePackageRecipeSource).new,
530 ppa_name = data.get('ppa_name', None)559 self.user, owner, data['name'],
531 ppa = owner.createPPA(ppa_name)560 data['recipe_text'], data['description'], data['distros'],
532 else:561 ppa, data['build_daily'])
533 ppa = data['daily_build_archive']
534 source_package_recipe = getUtility(
535 ISourcePackageRecipeSource).new(
536 self.user, owner, data['name'],
537 data['recipe_text'], data['description'], data['distros'],
538 ppa, data['build_daily'])
539 Store.of(source_package_recipe).flush()562 Store.of(source_package_recipe).flush()
540 except TooNewRecipeFormat:563 except ErrorHandled:
541 self.setFieldError(
542 'recipe_text',
543 'The recipe format version specified is not available.')
544 return
545 except ForbiddenInstructionError:
546 # XXX: bug=592513 We shouldn't be hardcoding "run" here.
547 self.setFieldError(
548 'recipe_text',
549 'The bzr-builder instruction "run" is not permitted here.')
550 return
551 except NoSuchBranch, e:
552 self.setFieldError(
553 'recipe_text', '%s is not a branch on Launchpad.' % e.name)
554 return
555 except PrivateBranchRecipe, e:
556 self.setFieldError('recipe_text', str(e))
557 return564 return
558565
559 self.next_url = canonical_url(source_package_recipe)566 self.next_url = canonical_url(source_package_recipe)
@@ -638,24 +645,10 @@
638 recipe = parser.parse()645 recipe = parser.parse()
639 if self.context.builder_recipe != recipe:646 if self.context.builder_recipe != recipe:
640 try:647 try:
641 self.context.setRecipeText(recipe_text)648 self.error_handler(self.context.setRecipeText, recipe_text)
642 changed = True649 changed = True
643 except TooNewRecipeFormat:650 except ErrorHandled:
644 self.setFieldError(651 return
645 'recipe_text',
646 'The recipe format version specified is not available.')
647 return
648 except ForbiddenInstructionError:
649 # XXX: bug=592513 We shouldn't be hardcoding "run" here.
650 self.setFieldError(
651 'recipe_text',
652 'The bzr-builder instruction "run" is not permitted'
653 ' here.')
654 return
655 except PrivateBranchRecipe, e:
656 self.setFieldError('recipe_text', str(e))
657 return
658
659652
660 distros = data.pop('distros')653 distros = data.pop('distros')
661 if distros != self.context.distroseries:654 if distros != self.context.distroseries:
662655
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2011-02-14 07:47:07 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2011-02-15 11:35:12 +0000
@@ -986,6 +986,19 @@
986 get_message_text(browser, 1),986 get_message_text(browser, 1),
987 'Recipe may not refer to private branch: %s' % bzr_identity)987 'Recipe may not refer to private branch: %s' % bzr_identity)
988988
989 def test_edit_recipe_no_branch(self):
990 # If a user tries to set a source package recipe to use a branch
991 # that isn't registred, they will get an error.
992 recipe = self.factory.makeSourcePackageRecipe(owner=self.user)
993 no_branch_recipe_text = recipe.recipe_text[:-4]
994 expected_name = recipe.base_branch.unique_name[:-3]
995 browser = self.getViewBrowser(recipe, '+edit')
996 browser.getControl('Recipe text').value = no_branch_recipe_text
997 browser.getControl('Update Recipe').click()
998 self.assertEqual(
999 get_message_text(browser, 1),
1000 'lp://dev/%s is not a branch on Launchpad.' % expected_name)
1001
989 def _test_edit_recipe_with_no_related_branches(self, recipe):1002 def _test_edit_recipe_with_no_related_branches(self, recipe):
990 # The Related Branches section should not appear if there are no1003 # The Related Branches section should not appear if there are no
991 # related branches.1004 # related branches.