wazoox has asked for the wisdom of the Perl Monks concerning the following question:

I can't get to set the scope properly so as to get $SIG{__WARN__} to behave a certain way in the module scope :
It works fine if I declare $SIG{__WARN__} in each sub of the module :
sub dbconnect { # params. my ($host, $port, $db, $user, $password)=@_; # redirect errors local $SIG{__WARN__} = sub { set_dberror(join('',@_))}; # connect or bust my $dbh = DBI->connect("DBI:mysql:database=$db:host=$host:port=$port", +$user,$password); return $dbh; }
But how may I avoid repeating the "local $SIG blah..." declaration for each sub?

Replies are listed 'Best First'.
Re: Problem of scope : $SIG{__WARN__} in a module...
by TedYoung (Deacon) on Dec 03, 2004 at 17:49 UTC

    Well, I am not sure why you would want to do that. If you are trying to capture DBI warnings/errors, you can give the DBI handle an error handler:

    $h->{RaiseError} = 1; # Turn all warnings into dies $h->{HandleError} = sub { set_dberror(join('',@_)) };

    Any errors generated by the dbh will be passed to your sub.

    If you really want to capture capture all warning for specific modules, you might try:

    package TrapWarnings; my %Modules; BEGIN { $SIG{__WARN__} = \ &trapWarning; } sub import { $Modules{caller} = 1; } sub trapWarning { my $caller = caller 1; if ($Module{$caller}) { set_dberror(join('',@_)) } else { warn @_ } } # Example use package Foo; use TrapWarnings; warn "foo";

    Well that code is completely untested. The general idea is everytime you import your TrapWarnings package, it adds that package to the list of packages to trap warnings for. When a warn happens, it checks the original caller to see if its package is in the list. If so, it traps it, otherwise, just re-warns it.

    This is far from ideal, but with creative use of the Carp module you might get where you want to be.

    Ted Young

    ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
      you can give the DBI handle an error handler:

      $h->{RaiseError} = 1; # Turn all warnings into dies
      $h->{HandleError} = sub { set_dberror(join('',@_)) };

      Any errors generated by the dbh will be passed to your sub.
      Errr... I don't really get it, how is this code supposed to work? won't it pass any warning to the sub, not only the dbh warnings ?
        Nope, this code will pass only DBH warnings to the set_dberror sub. You do it when when you initialize your DBH handle. Although, I don't think it handles connnection errors.

        Ted Young

        ($$<<$$=>$$<=>$$<=$$>>$$) always returns 1. :-)
Re: Problem of scope : $SIG{__WARN__} in a module...
by ysth (Canon) on Dec 03, 2004 at 18:15 UTC
    The local $SIG blah has to happen during runtime as you enter each sub, so there isn't a good way around it.
      You _could_ use Hook::LexWrap ... *grins*

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

        It's interesting, but... it's overkill there. I just aim to achieve a very clean output :)
Re: Problem of scope : $SIG{__WARN__} in a module...
by dragonchild (Archbishop) on Dec 03, 2004 at 17:41 UTC
    Put the declaration in the module scope? I dunno if you've tried that or not, but that would've been my first thought ...

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      Sure I tried it. unfortunately It doesn't work; either I put
      $SIG{__WARN__} = sub { set_dberror(join('',@_))};
      at the beginning of the module and it changes behaviour of all programs using it; I tried too
      local $SIG{__WARN__} = sub { set_dberror(join('',@_))};
      at the beginning of the module, but it just has no effect at all (the dbi errors are happily sput out on STDERR).

        Firstly, you can get DBI to stop spitting errors by:

        $dbh->{PrintError} = 0;

        Secondly, $SIG{__WARN__} doesn't automagically cover anything printed to STDERR, only things that result from a call to warn(), which happens to cause a print to STDERR.

        Finally, if neither of those applies, try:

        our $SIG{__WARN__} = sub { ... }

        For which I'm certain I will be flamed...

        radiantmatrix
        require General::Disclaimer;
        s//2fde04abe76c036c9074586c1/; while(m/(.)/g){print substr(' ,JPacehklnorstu',hex($1),1)}

Re: Problem of scope : $SIG{__WARN__} in a module...
by astroboy (Chaplain) on Dec 04, 2004 at 11:15 UTC
    Um, why are you using signals? Why not wrap all of your DBI code in a big eval and then deal with $@ if $@ exists (setting the handle's RaiseError when you connect first).
      That's a nice idea, but won't it somewhat slow down everything? I'll give it a try.
        Depends on the eval :-) string eval -> slow, block eval -> as fast as any other perl snippet

        Using eval of a block is the recommendation in the DBI documentation:

        The recommended way to implement robust transactions in Perl applications is to use RaiseError and eval { ... } (which is very fast, unlike eval "...").