Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Reloading modules- suppressing warnings works sometimes?

by JPaul (Hermit)
on Dec 20, 2003 at 20:46 UTC ( [id://316086]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings all;
I have another stoater here, that I'm sure some wise monk will be able to point out what exactly is going on - but truly has me confused.
First of all, I only recently discovered perl could literally reload modules on the fly. This is why I love perl. Not only is this so immensely cool - but perl makes it so easy. I firmly believe this is a sign of a superior language. But enough ass kissing.

My reload module is amply stolen from Apache::StatINC, as suggested here. This works perfectly for loading modules like it should - EXCEPT when it reloads itself. I suspect changing the package namespace on the function while it is running perhaps cocks up the lexical scope for the local - but I'm not entirely positive.

package foo; use strict; use warnings; sub bar { print "Works\n"; } 1; package reload; use strict; use warnings; sub reload { my ($PM) = @_; delete $INC{"$PM.pm"}; local $^W = 0; eval("require $PM;"); } 1; #Script #!/usr/bin/perl -w use strict; use reload; while(<STDIN>) print "Hit Enter.\n"; &reload::reload("foo"); &foo::bar(); &reload::reload("reload"); }
And, as you'll see soon enough, foo.pm reloads without any complaints, time and time again. However reload.pm leaves us with an undesireable "Subroutine redefined" warning.
All I want is for the warning to go away.
I could, simply, not use warnings at all - but I'd much rather find a way around this, if I can. Turning warnings off doesn't work either, I believe also related to scope.

-- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

Update: Apparently my terminology is guff. Not lexical scope, but dynamic.

Replies are listed 'Best First'.
Re: Reloading modules- suppressing warnings works sometimes?
by liz (Monsignor) on Dec 20, 2003 at 23:54 UTC
    Could it be that the modules that you are reloading, contain a use warnings; themselves, which would override the no warnings 'redefine'; inside the eval?

    If that's the case, I wouldn't know of a simple solution to this problem, other than reading the contents of the file to be required into a variable (e.g. $source) and then doing a:

    $source =~ s#(use warnings[^;]*)#$1;no warnings 'redefine'#sg; eval $source;

    This is pretty yucky, though. And may fail when the required file itself uses or requires other files.

    I guess more rigorous methods such as adding a subroutine handler to @INC or stealing CORE::require would be methods for handling this reliably.

    Or maybe simplify matters by installing a __WARN__ handler that would filter out the redefined warnings?

    Hope this helps.

    Liz

      If this was the case, then foo would not reload without warnings :)
      Still, good thoughts.

      JP,
      -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

Re: Reloading modules- suppressing warnings works sometimes?
by !1 (Hermit) on Dec 21, 2003 at 01:27 UTC

    Perhaps:

    package reload; use strict; use warnings; sub reload { my ($PM) = @_ or return; $PM =~ s!::!/!g; $PM .= ".pm"; delete $INC{$PM}; no strict 'refs'; no warnings 'redefine'; my $warnings = \&warnings::import; local *warnings::import = sub { &{$warnings}; unimport warnings "redefine"; }; eval { require $PM }; } 1;

    It should still work as before except it won't spew out redefine errors.

      Greetings;

      I have to admit - I don't fully understand entirely what the above does - I've only got a vague idea - but it works perfectly :)

      Well done :)

      JP,
      -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

        In a nutshell:

        sub reload { my ($PM) = @_ or return; $PM =~ s!::!/!g; $PM .= ".pm"; delete $INC{$PM};

        We return unless we get something to load. After that, we convert all ::'s to / and append .pm to the end of our module name because that's how it's stored in %INC and it keeps us from needing to eval string.

        no strict 'refs'; no warnings 'redefine';

        Just what it says.

        my $warnings = \&warnings::import; local *warnings::import = sub { &{$warnings}; unimport warnings "redefine"; };

        Ok. The magic happens here. $warnings contains a coderef to the warnings import method. It's very important that we keep this since warnings.pm may change between versions and we'd very much enjoy this not breaking just because we have a new warnings module. Anywho, the next line localizes the warnings::import typeglob. To this, we assign a subroutine that first calls our old warnings::import method with all the same parameters. Notice that I call it as &{$warnings} instead of $warnings->(). I could have done $warnings->(@_) as well, but what fun is that? Anyhow, since we imported whatever the calling script/module wanted we need to unimport the redefine warning immediately afterwards. Remember that this gets called from the module in question whenever it uses warnings. Thus it will affect the $^WARNING_BITS variable at that point.

        eval { require $PM }; }

        I love eval braces. Of course, it's used here because if require can't find the module it will kill the script. Just check the return from reload::reload to make certain the module did in fact load. Also of note is that at the end of the subroutine, warnings::import is magically restored to its old import method.

Re: Reloading modules- suppressing warnings works sometimes?
by tachyon (Chancellor) on Dec 21, 2003 at 02:59 UTC

    And, as you'll see soon enough, foo.pm reloads without any complaints, time and time again.

    Not on my system it doesn't. The issue is that you effectively have:

    { # one scope no warnings; # but the eval generates an inner scope { # where this over-rides the outer scope no warnings # or $^W = 0 - the inner scope reigns supreme. use warnings; } }

    This will remove the warnings from the reload.pm reloading itself but as noted this does not fix bar.

    #!/usr/bin/perl use strict; use warnings; use reload; do { print "Hit Enter.\n"; reload::reload("foo"); foo::bar(); reload::reload("reload"); } while <STDIN>; package reload; use warnings; use strict; { no warnings; sub reload { my ($PM) = @_; delete $INC{"$PM.pm"}; eval "require $PM"; } } 1; package foo; use strict; use warnings; sub bar { print "Works\n" } 1; __DATA__ C:\>perl test.pl Hit Enter. Works Hit Enter. Subroutine bar redefined at foo.pm line 4, <STDIN> line 1. Works Hit Enter. Subroutine bar redefined at foo.pm line 4, <STDIN> line 2. Works Hit Enter. Subroutine bar redefined at foo.pm line 4, <STDIN> line 3. Works Hit Enter. Subroutine bar redefined at foo.pm line 4, <STDIN> line 4. Works Hit Enter. Subroutine bar redefined at foo.pm line 4, <STDIN> line 5. Works ^C C:\>

    cheers

    tachyon

      Indeed, I messed up the copying in;

      In my local version of foo.pm, I have no 'use warnings;' only 'use strict;' - which, very certainly overrides the 'local $^W = 0;', and gives us the warn.
      (In other words, yea, the notice of bar being redefined is indeed there)

      Oops?
      -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

      Perhaps I made an error copying my working example into PerlMonks - but foo reloads without complaint. Only reload warns.
      I will have to look and see what you're seeing, vs what I'm seeing. Possibly a difference in perl versions, more likely I mucked up the copying in.

      Curious you make one work, and I make the other work, yet the twain do not meet.

      JP,
      -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

Re: Reloading modules- suppressing warnings works sometimes?
by chromatic (Archbishop) on Dec 20, 2003 at 21:33 UTC

    Have you tried no warnings 'redefine'; in your eval?

      Absolutely - with no apparent change in suppression of warnings.

      eval("no warnings 'redefine'; require $PM;");
      -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --
Re: Reloading modules- suppressing warnings works sometimes?
by JPaul (Hermit) on Dec 21, 2003 at 20:02 UTC
    So - what have we all learned?

    That it certainly pays to show a little more attention when copying things into PM questions - and more importantly, in looking at the differences between the modules in question in my case.
    The basic issue shown is that the eval'led reload module overrides the local suppression of warnings, as it should. I thank the monks for explaining this to me, I was kinda there, but not quite.

    However - the reason foo did not warn in my local examples - tested before posting - was simply because I didn't have 'use warnings;' in that code - and didn't spot that it wasn't there, ridiculously enough. And, in my haste, I manually typed the examples into my SOPW question - and magically added the 'use warnings;' which was the entire crux of the issue. Like, duh.

    JP,
    -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

      Hm, it would appear that you'll get these warnings in any module that does a use warings;, which /should/ be most modules. The fact that your module is apparently the only one that you tried that does that is rather sad, actualy.

      But removing the "use warnings" from reload.pm is clearly not a general solution, if this is indeed the problem. What happened, BTW, to my proposed (in the CB) solution of stealing Text::Template's _scrubpkg sub?


      Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

        Not sad - forgetful!
        I usually have strict/warnings in all scripts/modules, by default - Lord only knows how I managed to forget in the handful of modules that I've been using with this reloadable method.

        As for Text::Template's _scrubpkg, this much like !1's idea, looks like real voodoo to me - but thats simply because I don't "get it". Thats not to say I won't try it - I just haven't yet :)

        Thanks all;
        JP,
        -- Alexander Widdlemouse undid his bellybutton and his bum dropped off --

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://316086]
Approved by bart
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2024-04-24 15:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found