Difference between revisions of "Error Trapping with $ETRAP"
(Formatting and typos) |
|||
(One intermediate revision by one other user not shown) | |||
Line 9: | Line 9: | ||
− | Exception | + | Exception handling in Mumps |
----------------------------------- | ----------------------------------- | ||
Jawad | Jawad | ||
− | Subject: Exception | + | Subject: Exception handling in Mumps |
− | Exception | + | Exception handling can be performed in any modern programming language, I |
− | was wondering if there is any kind of exception | + | was wondering if there is any kind of exception handling in Mumps. |
− | + | Main goal is to avoid code crash. I don't think there are any "try" "Catch" | |
blocks in Mumps. I would like to know any approach that mumps programmers | blocks in Mumps. I would like to know any approach that mumps programmers | ||
use to avoid code crash. | use to avoid code crash. | ||
Line 35: | Line 35: | ||
N $ET,$ES | N $ET,$ES | ||
S $ET="HNDLR^MYRTN" ;do risky stuff | S $ET="HNDLR^MYRTN" ;do risky stuff | ||
− | + | HNDLR ; | |
;your own logic here | ;your own logic here | ||
D ^%ZTER ;if you want to log the error | D ^%ZTER ;if you want to log the error | ||
Line 47: | Line 47: | ||
kdtop | kdtop | ||
− | Greg, can you educate me a bit more about UNWIND. You say it "unwinds" the error stack and quits back to the calling routine. Does this mean that you don't really | + | Greg, can you educate me a bit more about UNWIND. You say it "unwinds" the error stack and quits back to the calling routine. Does this mean that you don't really need a QUIT on the line after it? Does it go back to the same line that caused the error, or the next line after? |
Thanks Kevin | Thanks Kevin | ||
Line 60: | Line 60: | ||
UNWIND ;Unwind stack for new error trap. Called by app code. | UNWIND ;Unwind stack for new error trap. Called by app code. | ||
− | S $ECODE="" S $ETRAP="D UNW^%ZTER Q:'$QUIT Q -9" S | + | S $ECODE="" S $ETRAP="D UNW^%ZTER Q:'$QUIT Q -9" S $ECODE=",U1," |
− | |||
UNW Q:$ESTACK>1 S $ECODE="" Q | UNW Q:$ESTACK>1 S $ECODE="" Q | ||
Line 69: | Line 68: | ||
to "D UNW^%ZTER Q:'$QUIT Q -9" and set the error code to ",U1,". | to "D UNW^%ZTER Q:'$QUIT Q -9" and set the error code to ",U1,". | ||
− | Okay, so what does THAT mean? Let's take it a piece at a time. | + | Okay, so what does THAT mean? Let's take it a piece at a time. The first step |
in handling any error will be to call UNW^%ZTER, then just QUIT if the | in handling any error will be to call UNW^%ZTER, then just QUIT if the | ||
code was not invoked as a function, or QUIT with the value -9 (if it | code was not invoked as a function, or QUIT with the value -9 (if it | ||
Line 107: | Line 106: | ||
Right, I'm confused already. | Right, I'm confused already. | ||
− | >Let's take it a piece at a time. | + | > Let's take it a piece at a time. The first step |
> in handling any error will be to call UNW^%ZTER, then just QUIT if the | > in handling any error will be to call UNW^%ZTER, then just QUIT if the | ||
> code was not invoked as a function, or QUIT with the value -9 (if it | > code was not invoked as a function, or QUIT with the value -9 (if it | ||
Line 131: | Line 130: | ||
I have seen documentation of the error codes... | I have seen documentation of the error codes... | ||
− | >The last piece of | + | > The last piece of |
> the puzzle is to look at UNW^%ZTER. The code here is just Q:$ESTACK>1 S | > the puzzle is to look at UNW^%ZTER. The code here is just Q:$ESTACK>1 S | ||
> $ECODE="" Q. That simply looks at $ESTACK, and if it's greater than 1 | > $ECODE="" Q. That simply looks at $ESTACK, and if it's greater than 1 | ||
Line 160: | Line 159: | ||
So in your example below: | So in your example below: | ||
− | 1>N $ET,$ES | + | 1> N $ET,$ES |
− | 2> S $ET="HNDLR^MYRTN" | + | 2> S $ET="HNDLR^MYRTN" |
− | 3> ;do risky stuff | + | 3> ;do risky stuff |
4> HNDLR ; | 4> HNDLR ; | ||
− | 5> ;your own logic here | + | 5> ;your own logic here |
− | 6> D ^%ZTER ;if you want to log the error | + | 6> D ^%ZTER ;if you want to log the error |
− | 7> D UNWIND^%ZTER | + | 7> D UNWIND^%ZTER |
− | 8> Q | + | 8> Q |
"the calling routine" would be where? I would say that UNWIND^%ZTER | "the calling routine" would be where? I would say that UNWIND^%ZTER | ||
Line 174: | Line 173: | ||
back somewhere around line 3 ("risky stuff") | back somewhere around line 3 ("risky stuff") | ||
− | >If you want to go back to where you left off when the error | + | > If you want to go back to where you left off when the error occurred, you |
− | > to write your code differently, but I recommend placing any code likely | + | > need to write your code differently, but I recommend placing any code likely |
> to raise an error in a DO block by itself. | > to raise an error in a DO block by itself. | ||
Line 189: | Line 188: | ||
... | ... | ||
− | Right, I'm confused already. | + | Right, I'm confused already. |
[Greg] | [Greg] | ||
My experience is that everyone finds it confusing. | My experience is that everyone finds it confusing. | ||
− | > Let's take it a piece at a time. | + | > Let's take it a piece at a time. The first step in handling any error will |
+ | > be to call UNW^%ZTER, then just QUIT if the code was not invoked as a | ||
+ | > function, or QUIT with the value -9 (if it was). | ||
− | If an error was encountered, and the user trapped it (as in your | + | If an error was encountered, and the user trapped it (as in your |
− | original example), and then called UNWIND^%ZTER, then I don't see what | + | original example), and then called UNWIND^%ZTER, then I don't see what |
− | setting $ETRAP does to handle the current problem. It seems only that | + | setting $ETRAP does to handle the current problem. It seems only that |
− | is will redirect the error handler for NEXT time. Furthermore, we have | + | is will redirect the error handler for NEXT time. Furthermore, we have |
− | set $ECODE="" initially, clearing the error state. | + | set $ECODE="" initially, clearing the error state. |
− | Let's see, after changing $ECODE to UNWIND^%ZTER, it sets another error | + | Let's see, after changing $ECODE to UNWIND^%ZTER, it sets another error |
− | state and then quits. Will this cause the $ECODE to be executed a | + | state and then quits. Will this cause the $ECODE to be executed a |
− | SECOND time? This time launching UNW^%ZTER?? | + | SECOND time? This time launching UNW^%ZTER?? |
[Greg] | [Greg] | ||
− | That's right. The current error is cleared (S $ECODE=""), but | + | That's right. The current error is cleared (S $ECODE=""), but |
errors are stacked, and there may be multiple outstanding errors. That | errors are stacked, and there may be multiple outstanding errors. That | ||
− | way, if | + | way, if there are further errors, they will be handled by UNW^%ZTER. |
− | >After resetting the error trap, set the error code to ",U1,". To make | + | >After resetting the error trap, set the error code to ",U1,". To make |
− | >sense of that, you need to know that errors are either part of the | + | >sense of that, you need to know that errors are either part of the |
− | >MUMPS standard and begin with M (e.g., M13), are vendor specific and | + | >MUMPS standard and begin with M (e.g., M13), are vendor specific and |
− | >begin with Z, or user defined and begin with U. The list of errors is | + | >begin with Z, or user defined and begin with U. The list of errors is |
− | >always comma delimited, hence ",U1," (Hmm...so I guess this means the | + | >always comma delimited, hence ",U1," (Hmm...so I guess this means the |
− | >SAC needs to prohibit use of U1.) We're almost there. | + | >SAC needs to prohibit use of U1.) We're almost there. |
− | I have seen documentation of the error codes... | + | I have seen documentation of the error codes... |
− | >The last piece of | + | > The last piece of |
− | > the puzzle is to look at UNW^%ZTER. The code here is just Q:$ESTACK>1 | + | > the puzzle is to look at UNW^%ZTER. The code here is just |
+ | > Q:$ESTACK>1 S $ECODE="" Q. | ||
+ | > That simply looks at $ESTACK, and if it's greater than 1 | ||
+ | > (there is more than one error in the error stack), it QUITs, | ||
+ | > effectively popping the stack. Finally, it clears the error condition | ||
+ | > with S $ECODE="" and QUITs. | ||
− | + | Well, it doesn't seem to be able to remove more than one level of error | |
− | + | code. I don't know if there would ever be a situation where more than | |
− | + | that would be needed. But the term "unwind" implies to me "back up as | |
− | + | far as needed" instead of just "pop 1 level back" | |
− | + | If $ESTACK>1 the error handler will quit, popping an error off the | |
− | + | stack, but if there are outstanding unhandled errors, it may be invoked | |
− | + | again. The idea is to allow this new error handler to consume the stack. | |
− | |||
− | + | > Okay, so how do you translate that into something more human friendly? | |
− | stack, | + | > At the most basic level, it's just popping off any errors that might |
− | + | > be left on the stack (without handling them), and clearing the error | |
+ | > condition. | ||
− | + | I had thought that there was some error logging here somehow, but I | |
− | + | don't see in this part of the code... | |
− | |||
− | |||
− | |||
− | I had thought that there was some error logging here somehow, but I | ||
− | don't see in this part of the code... | ||
[Greg] | [Greg] | ||
This code does not log the error. In order to log an error D ^%ZTER | This code does not log the error. In order to log an error D ^%ZTER | ||
− | (different entry point. In most cases, you will make the two calls one | + | (different entry point). In most cases, you will make the two calls one |
after another, i.e., | after another, i.e., | ||
− | D ^%ZTER ;log the error | + | * D ^%ZTER ;log the error |
− | D UNWIND^%ZTER ;unwind the stack (so you can continue with a clean | + | * D UNWIND^%ZTER ;unwind the stack (so you can continue with a clean slate) |
− | slate) | ||
− | > In response to your other question: No, you don't need a QUIT, but it | + | > In response to your other question: No, you don't need a QUIT, but it |
− | > doesn't hurt either. I tend to put a QUIT at the end of each section | + | > doesn't hurt either. I tend to put a QUIT at the end of each section |
− | > of code to prevent unintentional "fall through" errors, and when code | + | > of code to prevent unintentional "fall through" errors, and when code |
− | > is meant to fall through, I say so in a comment. | + | > is meant to fall through, I say so in a comment.When you call UNWIND^%ZTER |
+ | > control returns to the calling routine, not where you left off. | ||
− | + | So in your example below: | |
− | |||
− | + | 1> N $ET,$ES | |
+ | 2> S $ET="HNDLR^MYRTN" | ||
+ | 3> ;do risky stuff | ||
+ | 4> HNDLR ; | ||
+ | 5> ;your own logic here | ||
+ | 6> D ^%ZTER ;if you want to log the error | ||
+ | 7> D UNWIND^%ZTER | ||
+ | 8> Q | ||
− | + | "the calling routine" would be where? I would say that UNWIND^%ZTER | |
− | + | would quit back to the line after it was called, namely line 8. In | |
− | + | which case the Q would be needed. And the Q in line 8 would then jump | |
− | + | back somewhere around line 3 ("risky stuff") | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | "the calling routine" would be where? I would say that UNWIND^%ZTER | ||
− | would quit back to the line after it was called, namely line 8. In | ||
− | which case the Q would be needed. And the Q in line 8 would then jump | ||
− | back somewhere around line 3 ("risky stuff") | ||
[Greg] | [Greg] | ||
No, line 7 is part of the error handler. I mean control will be | No, line 7 is part of the error handler. I mean control will be | ||
− | transferred back to the block of code that invoked the | + | transferred back to the block of code that invoked the code causing the |
error to be generated. | error to be generated. | ||
− | >If you | + | >If you |
− | > want to go back to where you left off when the error | + | > want to go back to where you left off when the error occurred, you need |
− | >to write your code differently, but I recommend placing any code likely | + | >to write your code differently, but I recommend placing any code likely |
− | >to raise an error in a DO block by itself. | + | >to raise an error in a DO block by itself. |
− | I will say that I was pleased to learn about $QUIT. I hadn't | + | I will say that I was pleased to learn about $QUIT. I hadn't |
− | encountered that before. I looked it up and understand it now. I have | + | encountered that before. I looked it up and understand it now. I have |
− | had times where I could have used that... :-) | + | had times where I could have used that... :-) |
[Greg] | [Greg] | ||
Line 296: | Line 294: | ||
subject to change. | subject to change. | ||
− | Thanks | + | Thanks |
− | Kevin | + | Kevin |
Latest revision as of 18:46, 6 April 2010
Here is a thread regarding $ETRAP
Here is a quick and dirty solution:
do . new $etrap set $etrap="write ""(Invalid M Code!. Error Trapped.)"",! set $etrap="""",$ecode=""""" . do SomeThingRisky
Kevin
Exception handling in Mumps
-----------------------------------
Jawad
Subject: Exception handling in Mumps
Exception handling can be performed in any modern programming language, I was wondering if there is any kind of exception handling in Mumps. Main goal is to avoid code crash. I don't think there are any "try" "Catch" blocks in Mumps. I would like to know any approach that mumps programmers use to avoid code crash.
Jawad
-----------------------------------
Gregory Woodhouse
Yes. It's called error trapping in MUMPS. The basic pattern is to NEW $ETRAP and $ESTACK and set $ETRAP to the code reference for your handler. Control will be transferred there if an error is raised by setting $ECODE, or if an error occurs during execution. The value of $ECODE will be the error code or codes. You need to be sure to clear the error if you want execution to continue.
You can clear a single error by setting $ECODE to "", but Kernel provides a utility to unwind the error stack and quit back to the calling routine, D UNWIND^ %ZTER. To log an error simply D ^%ZTER.
So, your code may look something like this
N $ET,$ES S $ET="HNDLR^MYRTN" ;do risky stuff HNDLR ; ;your own logic here D ^%ZTER ;if you want to log the error D UNWIND^%ZTER Q
Gregory Woodhouse
-----------------------------------
kdtop
Greg, can you educate me a bit more about UNWIND. You say it "unwinds" the error stack and quits back to the calling routine. Does this mean that you don't really need a QUIT on the line after it? Does it go back to the same line that caused the error, or the next line after? Thanks Kevin
-----------------------------------
Woodhouse, Gregory J.
I always put QUIT at the end of my code. Call it a bad habit.
Anyway, I've never found a very clear explanation in the documentation of what UNWIND^%ZTER does. Let's look at the code:
UNWIND ;Unwind stack for new error trap. Called by app code. S $ECODE="" S $ETRAP="D UNW^%ZTER Q:'$QUIT Q -9" S $ECODE=",U1," UNW Q:$ESTACK>1 S $ECODE="" Q
This can be paraphrased as follows:
First, clear the current error (S $ECODE="") and set the error handler to "D UNW^%ZTER Q:'$QUIT Q -9" and set the error code to ",U1,".
Okay, so what does THAT mean? Let's take it a piece at a time. The first step in handling any error will be to call UNW^%ZTER, then just QUIT if the code was not invoked as a function, or QUIT with the value -9 (if it was). After resetting the error trap, set the error code to ",U1,". To make sense of that, you need to know that errors are either part of the MUMPS standard and begin with M (e.g., M13), are vendor specific and begin with Z, or user defined and begin with U. The list of errors is always comma delimited, hence ",U1," (Hmm...so I guess this means the SAC needs to prohibit use of U1.) We're almost there. The last piece of the puzzle is to look at UNW^%ZTER. The code here is just Q:$ESTACK>1 S $ECODE="" Q. That simply looks at $ESTACK, and if it's greater than 1 (there is more than one error in the error stack), it QUITs, effectively popping the stack. Finally, it clears the error condition with S $ECODE="" and QUITs.
Okay, so how do you translate that into something more human friendly? At the most basic level, it's just popping off any errors that might be left on the stack (without handling them), and clearing the error condition.
In response to your other question: No, you don't need a QUIT, but it doesn't hurt either. I tend to put a QUIT at the end of each section of code to prevent unintentional "fall through" errors, and when code is meant to fall through, I say so in a comment. When you call UNWIND^%ZTER control returns to the calling routine, not where you left off. If you want to go back to where you left off when the error occured, you need to write your code differently, but I recommend placing any code likely to raise an error in a DO block by itself.
-----------------------------------
kdtop
...
Right, I'm confused already.
> Let's take it a piece at a time. The first step > in handling any error will be to call UNW^%ZTER, then just QUIT if the > code was not invoked as a function, or QUIT with the value -9 (if it > was).
If an error was encountered, and the user trapped it (as in your original example), and then called UNWIND^%ZTER, then I don't see what setting $ETRAP does to handle the current problem. It seems only that is will redirect the error handler for NEXT time. Furthermore, we have set $ECODE="" initially, clearing the error state.
Let's see, after changing $ECODE to UNWIND^%ZTER, it sets another error state and then quits. Will this cause the $ECODE to be executed a SECOND time? This time launching UNW^%ZTER??
> After resetting the error trap, set the error code to ",U1,". To > make sense of that, you need to know that errors are either part of the > MUMPS standard and begin with M (e.g., M13), are vendor specific and > begin with Z, or user defined and begin with U. The list of errors is > always comma delimited, hence ",U1," (Hmm...so I guess this means the > SAC needs to prohibit use of U1.) We're almost there.
I have seen documentation of the error codes...
> The last piece of > the puzzle is to look at UNW^%ZTER. The code here is just Q:$ESTACK>1 S > $ECODE="" Q. That simply looks at $ESTACK, and if it's greater than 1 > (there is more than one error in the error stack), it QUITs, effectively > popping the stack. Finally, it clears the error condition with S > $ECODE="" and QUITs.
Well, it doesn't seem to be able to remove more than one level of error code. I don't know if there would ever be a situation where more than that would be needed. But the term "unwind" implies to me "back up as far as needed" instead of just "pop 1 level back"
> Okay, so how do you translate that into something more human friendly? > At the most basic level, it's just popping off any errors that might be > left on the stack (without handling them), and clearing the error > condition.
I had thought that there was some error logging here somehow, but I don't see in this part of the code...
> In response to your other question: No, you don't need a QUIT, but it > doesn't hurt either. I tend to put a QUIT at the end of each section of > code to prevent unintentional "fall through" errors, and when code is > meant to fall through, I say so in a comment.
>When you call UNWIND^%ZTER control returns to the calling routine, not where you left off.
So in your example below:
1> N $ET,$ES 2> S $ET="HNDLR^MYRTN" 3> ;do risky stuff 4> HNDLR ; 5> ;your own logic here 6> D ^%ZTER ;if you want to log the error 7> D UNWIND^%ZTER 8> Q
"the calling routine" would be where? I would say that UNWIND^%ZTER would quit back to the line after it was called, namely line 8. In which case the Q would be needed. And the Q in line 8 would then jump back somewhere around line 3 ("risky stuff")
> If you want to go back to where you left off when the error occurred, you > need to write your code differently, but I recommend placing any code likely > to raise an error in a DO block by itself.
I will say that I was pleased to learn about $QUIT. I hadn't encountered that before. I looked it up and understand it now. I have had times where I could have used that... :-)
Thanks Kevin
-----------------------------------
...
Right, I'm confused already.
[Greg] My experience is that everyone finds it confusing.
> Let's take it a piece at a time. The first step in handling any error will > be to call UNW^%ZTER, then just QUIT if the code was not invoked as a > function, or QUIT with the value -9 (if it was).
If an error was encountered, and the user trapped it (as in your original example), and then called UNWIND^%ZTER, then I don't see what setting $ETRAP does to handle the current problem. It seems only that is will redirect the error handler for NEXT time. Furthermore, we have set $ECODE="" initially, clearing the error state.
Let's see, after changing $ECODE to UNWIND^%ZTER, it sets another error state and then quits. Will this cause the $ECODE to be executed a SECOND time? This time launching UNW^%ZTER??
[Greg] That's right. The current error is cleared (S $ECODE=""), but errors are stacked, and there may be multiple outstanding errors. That way, if there are further errors, they will be handled by UNW^%ZTER.
>After resetting the error trap, set the error code to ",U1,". To make >sense of that, you need to know that errors are either part of the >MUMPS standard and begin with M (e.g., M13), are vendor specific and >begin with Z, or user defined and begin with U. The list of errors is >always comma delimited, hence ",U1," (Hmm...so I guess this means the >SAC needs to prohibit use of U1.) We're almost there.
I have seen documentation of the error codes...
> The last piece of > the puzzle is to look at UNW^%ZTER. The code here is just > Q:$ESTACK>1 S $ECODE="" Q. > That simply looks at $ESTACK, and if it's greater than 1 > (there is more than one error in the error stack), it QUITs, > effectively popping the stack. Finally, it clears the error condition > with S $ECODE="" and QUITs.
Well, it doesn't seem to be able to remove more than one level of error code. I don't know if there would ever be a situation where more than that would be needed. But the term "unwind" implies to me "back up as far as needed" instead of just "pop 1 level back"
If $ESTACK>1 the error handler will quit, popping an error off the stack, but if there are outstanding unhandled errors, it may be invoked again. The idea is to allow this new error handler to consume the stack.
> Okay, so how do you translate that into something more human friendly? > At the most basic level, it's just popping off any errors that might > be left on the stack (without handling them), and clearing the error > condition.
I had thought that there was some error logging here somehow, but I don't see in this part of the code...
[Greg] This code does not log the error. In order to log an error D ^%ZTER (different entry point). In most cases, you will make the two calls one after another, i.e.,
- D ^%ZTER ;log the error
- D UNWIND^%ZTER ;unwind the stack (so you can continue with a clean slate)
> In response to your other question: No, you don't need a QUIT, but it > doesn't hurt either. I tend to put a QUIT at the end of each section > of code to prevent unintentional "fall through" errors, and when code > is meant to fall through, I say so in a comment.When you call UNWIND^%ZTER > control returns to the calling routine, not where you left off.
So in your example below:
1> N $ET,$ES 2> S $ET="HNDLR^MYRTN" 3> ;do risky stuff 4> HNDLR ; 5> ;your own logic here 6> D ^%ZTER ;if you want to log the error 7> D UNWIND^%ZTER 8> Q
"the calling routine" would be where? I would say that UNWIND^%ZTER would quit back to the line after it was called, namely line 8. In which case the Q would be needed. And the Q in line 8 would then jump back somewhere around line 3 ("risky stuff")
[Greg] No, line 7 is part of the error handler. I mean control will be transferred back to the block of code that invoked the code causing the error to be generated.
>If you > want to go back to where you left off when the error occurred, you need >to write your code differently, but I recommend placing any code likely >to raise an error in a DO block by itself.
I will say that I was pleased to learn about $QUIT. I hadn't encountered that before. I looked it up and understand it now. I have had times where I could have used that... :-)
[Greg] Unfortunately, it is not currently allowed by the SAC, but that is subject to change.
Thanks Kevin