This was an amusing annoying little bug I recently discovered.

Imagine that module Foo in Foo.pm depends on module Bar:

package Foo; use Bar; 1;

But module Bar in Bar.pm is broken or missing or otherwise fails:

package Bar; 0;

Now, imagine that you have some code that checks if Foo is available using eval "require Foo" and, further, you wind up making that check more than once:

use Test::More tests => 2; for (1 .. 2) { eval "require Foo"; ok( $@, "Saw error loading Foo" ); }

And here's the result:

1..2 ok 1 - Saw error loading Foo not ok 2 - Saw error loading Foo # Failed test 'Saw error loading Foo' # at check_foo.pl line 5. # Looks like you failed 1 test of 2.

It turns out that $INC{Foo.pm} is set during require before Foo.pm is actually loaded (i.e. regardless of whether it succeeds or fails). The first eval prevents the code from dying, but $INC{Foo.pm} is still set so the second time around, require just shortcuts and returns 1.

So if eval "require Foo" fails because of something that Foo requires, you only get one chance to see if Foo is unavailable.

That suggests to me that it's wise to always use a subroutine that caches the first result just in case you ever need to check more than once:

my %have_module; sub have_module { my $mod = shift; if ( ! exists $have_module{$mod} ) { $have_module{$mod} = eval "require $mod"; } return $have_module{$mod}; }

Anyone know if some CPAN modules can help get this right?

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Replies are listed 'Best First'.
Re: How not to check if a module is installed (bug)
by tye (Sage) on Oct 24, 2007 at 15:36 UTC

    I think it is high time for a new special global to cache the return value from the first time a module was required.

    It seems a bug in Perl for "require Foo::Bar;" to succeed when Foo/Bar.pm is in a failing state (such as not existing). I understand the mechanism of this behavior but I still consider it a bug that a reasonable use of eval has the unfortunate consequnce.

    Now, with a new %LIB (or whatever it gets called) to hold the value returned by the first require, then subsequent requires can return the same thing as the first require did. This fixes the above bug. It also allows modules to usefully return a value to the script/module(s) that require them (something I've seen more than one good use for).

    - tye        

      While people might cringe at the idea, maybe that's something useful that could be hacked into UNIVERSAL::require.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: How not to check if a module is installed
by ikegami (Patriarch) on Oct 24, 2007 at 15:21 UTC

    The way it's done now allows for the mutual inclusion of two modules. The way you suggest would cause an infinite loop.

    The way it's done now takes into consideration that the re-running a module that failed in an uncontrolled fashion is a bad idea.

    If the module dies in a controled fashion (by returning a specific value or by throwing a specific exception indicating it's safe to reload the module), you can circumvent the %INC check by using do or by clearing the appropriate entry in %INC.

    (my $module = "$class.pm") =~ s{::}{/}g; if (!eval { require $module }) { die $@ if ((my $e = $@) !~ /.../); # Fix something ... # Try again delete $INC{$module}; require $module; }
      The way it's done now allows for the mutual inclusion of two modules. The way you suggest would cause an infinite loop.

      I didn't understand what you meant by this at first -- but after considering it, I think you're speaking of why %INC is set first. I agree. I wasn't suggesting changing it -- I just hadn't fully understood the consequences of what a failing "eval require" really does.

      More broadly, I think it shows a limitation of eval as an exception mechanism for module loading. If Foo or Bar die during require, they could easily leave the symbol table in an unstable state.

      As for your example, I'm actually not concerned with circumventing the %INC check. I just need to know if a module is installed (and working) -- and since I might ask that in more than one place in the code, some sort of cache of the first answer I get doing a require was what I needed.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: How not to check if a module is installed
by shmem (Chancellor) on Oct 24, 2007 at 15:10 UTC
    Why not just
    use Test::More tests => 2; for (1 .. 2) { eval "require Foo" or delete $INC{'Foo.pm'}; ok( $@, "Saw error loading Foo" ); } __END__ ok 1 - Saw error loading Foo ok 2 - Saw error loading Foo

    delete doesn't clear $@.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      For a real module, if warnings are on, that is likely to provoke "subroutine redefined" warnings.

      The example is intentionally simplistic. The real case was "eval require" in a subroutine in a module that later wound up being used inside a loop. It's not that I don't know how to work around this issue -- it just never occurred to me that this was the behavior of require.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: How not to check if a module is installed
by goibhniu (Hermit) on Oct 25, 2007 at 19:59 UTC

    I basically agree with Tye's reponse. However, since you asked so specifically:

    Anyone know if some CPAN modules can help get this right?

    This makes me think of memoize. Would it be possible to memoize require? Aside from the bootstrap mess of requiring Memoize in order to memoize require before you require other things, I'd suspect that there are many other problems lurking, which is why I agree that it should probably be fixed in Perl instead.

    update: and now that I think about it, it would prevent (or have to be turned off for) things like ikegami's:

    # Fix something ... # Try again


    I humbly seek wisdom.