The trap of eval { use Foo }

ferreira on 2007-07-16T18:22:59

When writing tests, the general boilerplate for optional tests which are totally skipped when a module is not installed, is:

eval "use Test::Pod 1.18";
plan skip_all => "Test::Pod 1.18 required for testing POD" if $@;

And then we remember that "eval STRING" must not be used where "eval BLOCK" may be enough. So I was tempted to write (and did it a few times):

eval { use Test::Pod 1.18 };
plan skip_all => "Test::Pod 1.18 required for testing POD" if $@;

This has the horrible disadvantage of not working ;-) The essential difference of "eval STRING" and "eval BLOCK" are that compilation issues are deferred in the former while happening earlier (at compile time) in the later. But the "use Foo" thing is a compile-time statement and then, as soon as Perl compiles the code, it executes the equivalent BEGIN block which will thus fail noisily.

$ perl -e "eval { use Foo };"
Can't locate Foo.pm in @INC (@INC contains: ... ) at -e line 1.
BEGIN failed--compilation aborted at -e line 1.
In turn, eval "use Foo" will do the right thing, executing the loading of the module at the opportune moment and storing the error message in $@ if that's the case. If you insist, you may use "eval BLOCK" yet in an expanded form:
eval {
    require Foo;
    Foo->VERSION(2.3);
    Foo->import
}

(That's exactly how rjbs did it in one of his recent distributions.)

Well, of course you knew that. But now that I wrote it, I don't even have to articulate it again some next time.

And now a shameless plug: one of the reasons Devel::Hide was written was to allow me to test code which should fail gracefully when certain modules are not present. Even in a machine, where those modules are indeed installed.

$ perl -Mblib t/99_pod.t
1..30
ok 1 - Foo.pm
...
ok 30 - Bar.pm

$ perl -Mblib -MDevel::Hide=Test::Pod t/99_pod.t
Devel::Hide hides Test/Pod.pm
1..0 # Skip Test::Pod 1.18 required for testing POD


Does perlcritic know about this?

Alias on 2007-07-17T01:38:12

That sounds like a fairly good candidate for a perlcritic rule.