Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Re^2: Understanding compiletime vs. runtime

by punkish (Priest)
on Feb 04, 2005 at 12:56 UTC ( #428042=note: print w/replies, xml ) Need Help??

in reply to Re: Understanding compiletime vs. runtime
in thread Understanding compiletime vs. runtime

full disclosure: The stuff in the BEGIN block is something I copied from another code (someone else's suggestion). It seemed to do what I wanted, so I haven't spent a lot of effort really understanding it.

Now, putting some effort to understand it, I believe what I am ending up doing is redirecting all the errors to foo.err, and all non-errors to foo.log. Which is ok. As quite a pleasant sideeffect of my ignorance, that _is_ what I want.

However -- I don't want to program to die as it seems to be doing. And, from your suggestion, it seems I will further insure that all warnings become dies. If, from what you say above, Mail::IMAPClient is just warning, why is the program die-ing?

Btw, per thor's suggestion, perl -c sends syntax OK to foo.err.

In the end -- I want there to be no die-ing at all, absolutely, unless a SIGINT is sent. In all cases other than a SIGINT, I want the error message written to foo.err and for the script to recover and merrily continue its way.

Since the above is a fair bit beyond my current understanding, I need some guidance on how to implement it.

Many thanks for your advice.

Replies are listed 'Best First'.
Re^3: Understanding compiletime vs. runtime
by tilly (Archbishop) on Feb 05, 2005 at 08:42 UTC
    Let me explain my suggestion in more detail.

    You're having errors in the eval, and you want to trap them. The eval will trap any die and store it in $@, which you're then processing. The problem is that a warn does not get trapped by eval, it just goes to STDERR. So the error is printed to that file, and then when you test $@ you have no sign that there was a problem.

    My suggestion turns warns into dies so that eval can trap them so that the code that you presented does something closer to what you say that you want. You're right that I've left warns as dies afterwards. That's fixable. In fact try the following variation of the eval and tell me whether it works better:

    # create an IMAP connection eval { # This is the inserted line. local $SIG{__WARN__} = sub {die @_}; $imap = Mail::IMAPClient->new( Server => $IMAP, User => $UNAME, Password => $PWD, Uid => 0, Debug => $DEBUGGING, ) or carp "Cannot connect to $IMAP as $UNAME: $@"; };
    I give you good odds that this will cause your eval to do what you want it to do, and it won't make warns into dies later in your program.
      In fact try the following variation of the eval and tell me whether it works better

      You the monk!

      Yes, it does. And, because of your patient explanation, I understand better what is going on. A few questions niggle my mind and remain --

      1. When I convert the warn into a die it gets trapped by eval. Why? I mean, why doesn't die behave the same way as warn... it is, after all, an error, and should be redirected to STDERR. Mind you, the way it is working _is_ how I want it to be. I just don't get this discrimination.
      2. When warn gets piped to STDERR, why does the script stop working? After all, it is just a warn and not a die. I would expect the script to warn and then continue working merrily. I've put it in an infinite loop with a sleep statement in there, so it should just take a breather and then try again. Except, if I don't modify eval as you have suggested above, the warn goes to STDERR and the script quits.
      3. Since the above modification works, it should also work if instead of warn I explicitly use die. In fact, I am going to try that right away and see if it does so. In that case the local modification of SIG{__WARN__} should not be necessary.
      4. And finally, I am assuming I can convert other SIGs the same way. In fact, I don't want the script to die ever unless CTRL-C/BREAK is pressed (SIGINT) or the computer is shutdown. Any caveats to that?
      In any case, muchas gracias por su ayuda.
        The answer to all of your questions is that you have slightly wrong mental models of how Perl works, and they don't have room to explain what Perl is doing in this case. The solution is to correct your mental model of what Perl does, and then you can correct your mental model of what is happening in your program.

        So let me state facts about how Perl works.

        • STDERR is a filehandle, just like STDOUT, just like ones you open on files. Nothing more, nothing less. It is somewhat special in that it happens to be a filehandle on which error messages are likely to be written, but nothing special happens when you write to it.
        • When you warn, Perl does something like this:
          sub warn { my @messages = @_; unless (@messages) { @messages = "Warning: something's wrong"; } if ($messages[-1] !~ /\n$/) { my ($package, $file, $line) = caller(); $messages[-1] .= " at $file line $line\n"; } if ($SIG{__WARN__}) { $SIG{__WARN__}->(@messages); } else { print STDERR @messages; } }
        • When you die, Perl does something like this:
          sub die { my @messages = @_; unless (@messages) { @messages = "Warning: something's wrong"; } if ($messages[-1] !~ /\n$/) { my ($package, $file, $line) = caller(); $messages[-1] .= " at $file line $line\n"; } if ($SIG{__DIE__}) { $SIG{__DIE__}->(@messages); } foreach level (travel up the callstack) { if (this level is trapping exceptions) { $@ = join "", @messages; transfer control to where that's caught. } } unless ($SIG{__DIE__}) { print STDERR @messages; exit(255); } }
        • eval catches exceptions.

        Now let's try to answer your questions.
        1. Why is die trapped by eval but not warn? Well in the pseudo-code above, you can see that die triggers an exception, and eval traps that exception. warn does not trigger an exception. Therefore only die is caught.

          Going to a higher level of explanation, it is because Larry Wall thought that it would be more useful to trap die than warn. And it is useful because the primary thing that die does is change the flow of the program, and someone might want to regain control somewhere. By contrast the primary thing that warn does is print to STDERR, and people can already control STDERR.

        2. Why does my script stop working? I don't know. I know that the code that you've presented shouldn't have it stop working. It may be that goto_sleep doesn't do what the name suggests. It may be that some step inside of process fails if you don't have $imap. Perhaps you want to return if $@ is set because the rest of process() will fail?

        3. My code should work if instead of converting warn into die, I just use die in the first place. Yes. I only suggested the other since I didn't know how much control you have over Mail::IMAPClient.

        4. Any caveats to converting other SIGs the same way? Well, you have to track them down. And most of them won't stop your program anyways, so I wouldn't worry about it. You'll probably get farther if you avoid exit and use eval liberally.

          However I'll give you a random warning. A common problem with Unix systems is that people write daemons like this and expect them to always be up. Then months later the system reboots, and people have forgotten about the necessary daemons. Then things break and it is really hard to track down why.

          The solution is that if you write something that you really want to always be running, just have it try to launch regularly from cron, check if it needs to run (checking for a a control file is a common way to do this), and if it does, then run it. Otherwise have it quietly exit.

        Hopefully that helps.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://428042]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (2)
As of 2023-03-26 12:28 GMT
Find Nodes?
    Voting Booth?
    Which type of climate do you prefer to live in?

    Results (63 votes). Check out past polls.