Merge lp:~abentley/launchpad/recipe-errors into lp:launchpad
Status: | Rejected | ||||
---|---|---|---|---|---|
Rejected by: | Aaron Bentley | ||||
Proposed branch: | lp:~abentley/launchpad/recipe-errors | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
126 lines (+48/-35) 2 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+37/-35) lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+11/-0) |
||||
To merge this branch: | bzr merge lp:~abentley/launchpad/recipe-errors | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | code | Needs Fixing | |
Review via email: mp+42305@code.launchpad.net |
Commit message
Handle bad branch for recipe editing
Description of the change
= Summary =
Fix bug #683321: Editing recipe oopses if invalid branch used
== Proposed fix ==
Unify error handling into a context manager
== Pre-implementation notes ==
None
== Implementation details ==
With this context manager, two operations (setRecipeText and
ISourcePackageR
handled the same way. This ensures that adding and editing recipes has uniform
exception handling.
== Tests ==
bin/test -t edit_recipe_
== Demo and Q/A ==
Create a recipe. Edit it to set the branch to an invalid value. You should
get an error message, not an OOPS.
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
./lib/lp/
338: Line exceeds 78 characters.
./lib/lp/
370: E501 line too long (80 characters)
752: E501 line too long (85 characters)
766: E501 line too long (89 characters)
778: E501 line too long (85 characters)
793: E501 line too long (89 characters)
809: E501 line too long (85 characters)
370: Line exceeds 78 characters.
752: Line exceeds 78 characters.
766: Line exceeds 78 characters.
778: Line exceeds 78 characters.
793: Line exceeds 78 characters.
796: Line exceeds 78 characters.
809: Line exceeds 78 characters.
Unmerged revisions
- 12002. By Aaron Bentley
-
Unify error handling for recipe text
This is inventive and thus a little shocking. I suppose the real problem is that the textual representations of these exception types are inadequate. Otherwise, all you needed here would be a tuple of exception types that represent input errors!
If you had such a tuple and usable error strings, you'd be able to do this in both use-cases instead of creating a context manager:
try:
pass # (or more likely, something useful)
except recipe_ input_errors, e:
self.setFieldE rror('recipe_ text', str(e))
But I'm sure you would have done that if you had the option of improving the text representations of these exception types.
Still, I can't help feeling that the solution is not the best one you could get here: on the one hand your context manager interacts with the view, which actually fits better in the calling code. On the other it acts as an overlay to a number of exception classes in a "poor man's dynamic dispatch" pattern. And on the gripping hand, it makes a three-way control-flow decision (raise, return, continue) that context managers weren't designed for. If that's one hand too many, well, that's my point!
The context-manager API forces you to let the control-flow decision spill back into the calling code. Which obscures control flow: a "raise" happens in the "with" body, an "except" may happen in the context manager, an exception may be re-raised in the context-manager language support based on the exit handler's return value, and then the caller either returns or continues based on a variable set in the exit handler. That's a lot for a reader to follow when trying to figure out what the view code will do—or what effects a prospective code change will have. Be nice to the future hacker who's in unfamiliar with the code and in a hurry to fix something: it may be you two years from now.
As either an alternative to the context manager or a refinement of it, have you considered extracting just the identification and representation of the input errors?
try:
pass # (view work here)
except Exception, e: recipe_ input_error( e)
self. setFieldError( 'recipe_ text', str(e))
input_error = represent_
if input_error is None:
# Unexpected problem.
raise
else:
# Known problem with the input.
return
Even in combination with the context manager, separating the representation of the errors would make the control flow more concise.
If you do prefer to continue with the context-manager approach, I would ask for a few changes:
* Unit-test the context manager.
* Do exception representation in exception classes where possible.
* Rename return_now so it doesn't make assumptions about the calling code.
* Address the XXX one way or another.
Thanks for including "lint" output, by the way. Fixing the lint after review helps keep the diff clean.