6

In answering this question, I suggested that the OP open a stream at the beginning of his notebook and close it at the end. However, if an Abort is generated, the stream will be left open, and will cause havoc if they attempt to open it again without checking first. If the stream was only required for a single function, the solution would be straightforward, but it's required for the entire notebook. Obviously, a check can be added to see if the stream is already open, but is there a way to tie into the global Abort handler so that this type of problem can be handled globally?

Edit: to be specific, I'm looking for a way to run arbitrary code when an Abort occurs whether, or not, the code is currently running inside of CheckAbort. Essentially, I'd like to set a global Abort handler, if possible. If this exists at the notebook level, then even better.

Community
  • 1
  • 1
rcollyer
  • 10,475
  • 4
  • 48
  • 75

2 Answers2

9

As an alternative, and if you want to localize the effect to a single notebook, you can do something along these lines:

SetOptions[EvaluationNotebook[], 
   CellEvaluationFunction -> 
     (ToExpression[#, StandardForm,
         Function[
            Null,
            Module[{aborted = $Aborted},
              Internal`WithLocalSettings[
                 Null,
                 aborted = (ReleaseHold[Most[Hold[##]]];Last[Hold[##]]),
                 AbortProtect[
                   If[aborted === $Aborted,
                      Print["Did cleanup"]; Abort[]
                   ]]]], 
            HoldAll]] &)
]

NOTE: rewritten to incorporate the suggestion of @Alexey

NOTE 2 Modified to accommodate several inputs in a single cell. In that case, all outputs but the last are suppressed

where you replace the Print["Did cleanup"] code with whatever cleanup code you have.

Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • I was just looking at that functionality. – rcollyer Oct 12 '11 at 17:39
  • 2
    I think it is worth to wrap `Print["Did cleanup"]; Abort[]` in `AbortProtect`. In addition, `CheckAbort` does not allow to catch interrupts. In this way it is reasonable to use ``Internal`WithLocalSettings`` instead of `CheckAbort`. – Alexey Popkov Oct 12 '11 at 18:02
  • 1
    @yoda, I'm going with Leonid's answer only because it can be contained to one notebook. – rcollyer Oct 12 '11 at 18:17
  • @Alexey Yes, that makes sense, thanks. I implemented the necessary changes - see the edit. – Leonid Shifrin Oct 12 '11 at 18:31
  • @Leonid I am not sure how `ToExpression` works in this case. For example, `SetOptions[EvaluationNotebook[],CellEvaluationFunction->(ToExpression[#,StandardForm,Function[expr,Print[Hold[expr]],HoldAll]]&)]` prints boxes instead of expression - and even without the `Hold` wrapper! I do not understand, why? – Alexey Popkov Oct 12 '11 at 18:51
  • @Alexey I can't reproduce your problem. For me, when I enter `1+1`, it duly prints `Hold[1+1]`. Start with a freshly created notebook and see if the problem is still there - I suspect that it is an artifact of some handler that you set previously. – Leonid Shifrin Oct 12 '11 at 19:20
  • 1
    @Leonid After restarting *Mathematica* the problem disappear. But try to enter two expressions on separate lines in the cell - only first is printed. – Alexey Popkov Oct 12 '11 at 19:34
  • 1
    @Alexey Ok, that's a good catch. Partial solution would be to use `(ToExpression[#, StandardForm, Function[Null, Print[Hold[##]], HoldAll]] &)`, but this would lead to a different output, since all the input lines will be combined to produce a single output, being a `Sequence` of individual outputs. I don't know at the moment how to remedy that. But I will at least modify the answer, since currently only the first input is indeed executed. – Leonid Shifrin Oct 12 '11 at 19:53
  • @Leonid The problem is very interesting. I faced it before when was thinking on [this question](http://stackoverflow.com/questions/5625648/how-to-abort-evaluation-of-a-sequence-of-inputs) but still have no solution. One idea is that we should handle the Cell expression inside of the FrontEnd in some way - but it is undocumented. Another idea is that there may be a way to generate multiple outputs just from the kernel - John Fultz gave one `MathLink` example [here](http://groups.google.com/group/comp.soft-sys.math.mathematica/msg/fd5232e8bf908c8c). But it seems to be undocumented too. – Alexey Popkov Oct 12 '11 at 20:24
  • 1
    @Alexey Indeed. I would not so much worry about a feature being undocumented, the main question is whether or not it has been used internally often enough to guarantee that it will stay in future versions. Have no time to dig deeper into this now, alas, but if you find something out, I'd certainly be interested in your results (and probably others as well). – Leonid Shifrin Oct 12 '11 at 20:35
8

A very simple way would be to issue the following at the start of the file:

Close /@ Streams[] // Quiet

The standard streams stdout and stderr cannot be closed and you silence the warning with Quiet. However, this also assumes that you don't have any open streams that you care about.

To handle an Abort and close the stream, you can modify $Post like:

$Post := If[# === $Aborted, Close[strm], #] &

where strm is the stream that you opened.

abcd
  • 41,765
  • 7
  • 81
  • 98
  • True, and answers the specific question. But, is there a general method for running arbitrary global code when an `Abort` occurs? – rcollyer Oct 12 '11 at 17:13
  • 1
    [`$Pre`](http://reference.wolfram.com/mathematica/ref/$Pre.html) could also be used to wrap the function call in `CheckAbort`. – rcollyer Oct 12 '11 at 17:45
  • 1
    As I'm upvoting your answers, I'm not helping myself overtake your total. – rcollyer Oct 12 '11 at 17:51