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

I can't seem to figure this one out, maybe someone out there can help a bit...

Within an import function in a package I'm working on, one of the things I'm trying to accomplish is re-defining a named routine in the caller via a string eval (don't ask ;) ). In doing so, and when warnings are used relative to the calling package, I get the infamous 'redefine' warning. The question I have is: how do I turn this off within a calling package, but the calling package only?

I have tried supplementing the re-definition with no warnings 'redefine'; from everywhere I can think of. The problem seems that because the warnings module is so reliant on caller() that I can't seem to trick it into turning off the 'redefine' warning in the calling package of my package - where it will always turn it off in my package. I've also tried removing stuff from %INC, dropping the function from the symbol table of the calling package, all the way down to trying a source filter! Nothing seems to help.

I don't see this being such a hard thing and maybe I'm tired, but I still need help. Anything would be appreciated.

Example:
package MyPackage; use warnings; use strict; sub import { my ( $caller ) = caller; my $cpackage = $caller; $cpackage =~ s/::/\//g; my ( undef, $function ) = ( shift, shift ); # I know, I know # # This is just an example. I know re-defining # BEGIN in the caller has it's problems, but I'm # trying to keep it simple for this snippet # { no strict 'refs'; eval( qq^ sub ${caller}::BEGIN { delete( $INC{'${cpackage}\.pm'} ); # # I've tried the following, too: # no warnings 'redefine'; # delete( %{ "${caller}::${function}" } ); # FILTER{} # etc. # # I get the re-define warning here require ${caller}; } ^); } }


Thanks!

---------
perl -le '$.=[qw(104 97 124 124 116 97)];*p=sub{[@{$_[0]},(45)x 2]};*d=sub{[(45)x 2,@{$_[0]}]};print map{chr}@{p(d($.))}'

Replies are listed 'Best First'.
Re: unimport warnings in another package
by diotalevi (Canon) on Mar 21, 2007 at 07:10 UTC

    Core warnings like this are typically enabled and disabled with a lexical pragma. With the -W command line parameter, you can force warnings to be turned on even where you didn't request it. If you want to disable warnings in "other" code you'd need to hook the global $SIG{__WARN__}. To get around this specific warning ... lesse... you could just empty out the target symbol table. This clobbers everything below your target and allows for some really bad action at a distance.

    In short, all the fixes I'm thinking of now are really, really terrible ideas. Please don't do them at work. I'll buy you a mocha.

    %{$caller . '::} = (); # clobber our caller. local $SIG{__WARN__} = 'IGNORE'; # temporarily turn off all warnings ( +this is the least bad, imho)

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      Thanks for the reply ... I thought of that, too, but I don't really want to turn off *all* warnings, just the one I want in the scope I want. I suppose I can store off the original handler, go crazy, and re-assign the warn handler to turn warnings back 'on'. I suppose I don't want to go down that road if I don't have to, though.

      Thanks again, though!

      ---------
      perl -le '$.=[qw(104 97 124 124 116 97)];*p=sub{[@{$_[0]},(45)x 2]};*d=sub{[(45)x 2,@{$_[0]}]};print map{chr}@{p(d($.))}'

        If you just localize it with local $SIG{__WARN__} = ... then it'll clean itself up without any work required by you. Further... if you really wanted to you could assign your own sub and filter out just the warnings you want to ignore.

        local $SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /^Subroutine .+ redefined/; }

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: unimport warnings in another package
by shigetsu (Hermit) on Mar 21, 2007 at 06:33 UTC
      Thanks for the idea, but I was hoping for something I can use that doesn't create a dependency on a non-core module. Maybe I'll dig into it's guts to see how it's doing things though.

      ---------
      perl -le '$.=[qw(104 97 124 124 116 97)];*p=sub{[@{$_[0]},(45)x 2]};*d=sub{[(45)x 2,@{$_[0]}]};print map{chr}@{p(d($.))}'
Re: unimport warnings in another package
by ysth (Canon) on Mar 21, 2007 at 06:44 UTC
    Could you give more of an example of what you are trying to do?

    For instance, what the caller looks like?

    I'm guessing maybe you meant to have a \ before the $ in "delete $INC..."?

    The BEGIN baffles me; you can't redefine a BEGIN block, it's run as soon as it's compiled. The ${caller}:: part does absolutely nothing.

    Have you tried a glob assignment?

      Thanks, ysth!

       > > I'm guessing maybe you meant to have a \ before the $ in "delete $INC..."?
      Oops, ya.

       > > The BEGIN baffles me; you can't redefine a BEGIN block, it's run as soon as it's compiled. The ${caller}:: part does absolutely nothing.
      That's what I thought, too. The problem really is that at import time, I'm trying to dig into functions of the caller that haven't been compiled into the symbol table at the time. In other words, the list of functions passed to the import represent functions the caller wants me to change, yet because they're called at compile-execution time, too, nothings there. The only function that's assigned at the time was BEGIN, so I gave it a try. With the re-require of the calling package, the functions get assigned to the symbol table and I can change them at my will anytime after that. (did that confuse things more?

       > > Have you tried a glob assignment?
      I did, and although that looses the warning - I hit the problem I mention earlier. Essentially, I'm trying to add some aspect-ish slicing to any calling package, where to effectively do so, I need to have the functions already defined - does that make sense? Where if I simply use a GLOB assignment, I overwrite the entire function; which isn't really my goal.

      'Caller' example
      package Caller; use strict; use warnings; # # The idea being that I want MyPackage to do something # with 'use_this_function'. Because I can't guarentee the # function will be defined at the point of import, I'm trying # to force the compilation of the module in order to get # the assignment in the symbol table so I can change it appropriately # (I use that word lightly here) # use MyPackage qw( use_this_function ); # From earlier example function dont_use_this_function { return; } function use_this_function { return; } 1;


      Thanks again for your help!

      ---------
      perl -le '$.=[qw(104 97 124 124 116 97)];*p=sub{[@{$_[0]},(45)x 2]};*d=sub{[(45)x 2,@{$_[0]}]};print map{chr}@{p(d($.))}'
Re: unimport warnings in another package
by rhesa (Vicar) on Mar 21, 2007 at 16:09 UTC
    Would it be possible to do the actual re-defining at a later stage (say, CHECK or INIT)?

    Something like this:

    package ImportLater; use strict; use warnings; our %overrides; sub import { my $class = shift; my @functions = @_; my $cpkg = caller; $overrides{$cpkg} = \@functions; return; } # For demonstration purposes only. This is where you'd do your stuff sub override_functions { for my $pkg( keys %overrides ) { print "$pkg: @{$overrides{$pkg}}\n"; foreach my $func ( @{$overrides{$pkg}} ) { no warnings 'redefine'; no strict 'refs'; my $orig = \&{"$pkg\::$func"}; *{"$pkg\::$func"} = sub { print "before $func\n"; return $orig->(@_); }; } } } INIT { override_functions(); } 1;
    Use it like this:
    #!/usr/bin/perl use strict; use warnings; use ImportLater qw/ foo bar /; sub foo { print "I'm foo"; } foo();
    Running that outputs:
    main: foo bar before foo I'm foo
Re: unimport warnings in another package
by Anonymous Monk on Mar 21, 2007 at 06:47 UTC
    I can't seem to trick it into turning off

    You should take the hint

      heh... that's no fun, though

      ---------
      perl -le '$.=[qw(104 97 124 124 116 97)];*p=sub{[@{$_[0]},(45)x 2]};*d=sub{[(45)x 2,@{$_[0]}]};print map{chr}@{p(d($.))}'