[Zope3-dev] best way to deal with dependency errors
Jim Fulton
jim at zope.com
Tue Feb 10 04:38:26 EST 2004
Anthony Baxter wrote:
> At the moment if you try to delete a registered service or utility,
> you get a system error. The traceback shows a dependency error on
> the registration object. This could be handled by one of two ways:
>
> before deleting, auto-delete the registration(s)
> or
> make the delete code catch dependencyerror and present a nice
> message.
>
> Which is preferred? Or both?
Neither. :)
I want a more general solution. I'm willing to live with the current
blight until that happens, especially since I'd like to get rid
(or redo) the dependency framework soon.
Here's the issue: when we generate an event, there may be more than
one subscriber that has an issue with it. There needs to be a way of
resolving these issues and the code generating the event shouldn't
be responsible for resolving the issues specifically, although there
should be a general way for it to do so.
The thing that makes it hard is that we want to be able to collect
multiple issues. Exceptions don't work very well for this, unless
there is something willing to collect them together.
One idea that Steve and I came up with was to generate tentative events
to collect issues. Something like:
# publish a tentative event
event = Tentative(ObjectRemovedEvent(ob))
publish(event)
if event,issues:
# There were issues, do something
....
# Now that we've dealt with the issues, publish the event
publish(event.event)
If we want to raise issues, we create two subscribers. One subscriber
subscribes to tentative events and one subscribes to normal events.
The tentative event subscribers can add issues to the event to be resolved:
def notify_tentative_remove(tentative_event):
real_event = tentative_event.event
if has_dependents(event.object):
tentative_event.addIssue(DependencyIssue(event.object))
This seems rather complicated for the code that generates events,
especially since, I suppose, it could, potentially, apply to most events.
Another possibility is to provide a specialized event channel that
can help with the tentative events. The application code would just publish
the normal event. The channel gets the event and sends a tentative event
to it's subscribers. If there are no issues it sends the regular event.
If there are issues, the event channel could just raise a special
exception (e.g. EventRejected). The exception could contain the issues.
At that point, the application code can catch the exception
and try to deal with the issues. How would the application code deal with
the issues? Well, in a number of ways. First, let's assume that EventRejected
exceptions have event and issues attributes. The issues attribute is an iterable
of issues.
We can suppose some properties of issues:
- We can display them by displaying views of them
- Some issues might have (or be adaptable to objects that have) a
reolve method. If we have a resolve method, we can call it to resolve the
issue.
- Some issues might require confirmation. If this is the case, the user
should be asked to confirm the action necessary to resolve the issue.
if this is the case, the application should display the issue to the user
and get them to confirm it before calling the fixup method.
- Some issues may be unresolvable. I'm not sure we should allow this.
Perhaps this could be useful in the case of issues that can't be simply
resolved but that could be resolved through some more involved action
by the user. This is a little bit risky, as you can end up in a position
in which you can't resolve an issue and can't make progress.
If all of the issues are resolvable without confirmation, then the event
channel can just resolve them. I suppose that if one of the events are
not resolveable, the channel could raise a different exception
(e.g. EventError) that isn't caught by the app. In that case, the
app only needs to worry about confirmation.
So, careful application code might look something like this:
try:
publish(ObjectRemovedEvent(ob))
except EventRejected, exc:
# we have some issues that can be resolved, but these
# need to be confirmed. We'll check to see if the user has
# confirmed the issues. We'll call a helper view that checks
# this for us:
if getViewProviding(exc, request, IVerifyConfirmation):
# The user has said OK, do it. Publish a confirmed
# event:
publish(ConfirmedEvent(ObjectRemovedEvent(ob), exc.issues)
else:
# Raise a confirmation exception that, when viewed displays
# a conformation message and returns to the request URL.
raise ConfirmationRequired(exc)
The ConfirmationRequired exception has a view that:
- Displays the issues and a confirmation button
- Includes hidden fields that identify the issue.
This is needed to make sure that we have confirmed all
of the same issues when the application is reexecuted.
The IVerifyConfirmation view compares the information in the hidden
request data (setup by the ConfirmationRequired view) with the issues
in the exception.to make sure that all of the issues in the esception
have been confirmed.
When the event channnel gets a ConfirmedEvent, it:
- Sends a tentative event to its subscribers, and collects all of the
issues.
- Compares the issues collected with the issues passed to it.
o If the issues are the same, it knows that they have been confirmed
and calls resolve on each of them
o Otherwise, it raises EventRejected.
If the application doesn't catch the exception, then the exception will
be displayed to the user. In doing so it will display the issues.
It will display the issues with a different view than the one used for
confirmations. The issue views could provide UIs or links to UIs for resolving
the issues. In this way, even if the application doesn't handle the issues,
the issues could lead the user to take actions that will resolve the issues.
Having done so, the user can then redo the application action, which should
succeed.
Whew! But I think this is workable. :)
Thoughts?
Jim
--
Jim Fulton mailto:jim at zope.com Python Powered!
CTO (540) 361-1714 http://www.python.org
Zope Corporation http://www.zope.com http://www.zope.org
More information about the Zope3-dev
mailing list