Failed compilations can be partially successful

Apparently Perl doesn't clean up after a failed compile. However far it got sticks around. I didn't discover this myself, but here's an example:


eval <<'HERE';
package Weird;
use strict;
sub foo { print "hi\n" }
sub bar { print "$bar\n" }
1;
HERE

print "\$@ -> $@";

Weird::foo();
Weird::bar();

The Weird package won't compile all the way because it has a strict violation in Weird::bar(). Here's the output, which shows that Weird::foo() was defined and sticks around:

$@ -> Global symbol "$bar" requires explicit package name at (eval 1) line 4.
hi
Variable "$bar" is not imported at (eval 1) line 4.
Undefined subroutine &Weird::bar called at test.pl line 14.

I'd never really thought about this because it was never much of a problem for me.

7 Comments

That could have implications for templating systems that embed Perl directly. They could compile a subroutine that was placed in the template, stick it in the package namespace, and then hit a syntax error later.

But other than taking up a little extra memory before erroring out, I'm not sure that has any practical issues.

It becomes a problem if you later use Weird->can("foo") as an indicator of whether the Weird package loaded successfully.

This is part of the reason Test::Most can die on fail. Years ago I was struggling to figure out a problem where some perfectly sensible code was mysteriously failing a test, but the there appeared to be nothing wrong with the code. As it turns out, at the top of my test, use_ok was also failing, but so many passing tests scrolled past that I didn't see that. I only saw a very mysterious failure at the end. Another programmer and I wasted a lot of time trying to figure out what was wrong, but didn't realize that Perl doesn't clean up after itself in this case.

That's part of the reason I advocate simply to use a module instead of use_ok, and with dying on test failure, you have another stopgap measure to help trap that (the latter argument being less persuasive, I admit).

I like that you can tell Test::Most to die on fail, but don't like that it will still run the subsequent test.

See this pseudo-code where the "run" function dies on all errors:

lives_ok { run("mount server:/abc /mnt/abc") "Mounting";
lives_ok { run("dd if=/dev/zero of=/mnt/abc/file count=1M") } "dd";

This will still run the dd and only abort after that. I haven't figured out a good way to deal with that except for adding an "or die ..." after every critical test.

I like that you can tell Test::Most to die on fail, but don't like that it will still run the subsequent test.

See this pseudo-code where the "run" function dies on all errors:

lives_ok { run("mount server:/abc /mnt/abc") "Mounting";
lives_ok { run("dd if=/dev/zero of=/mnt/abc/file count=1M") } "dd";

This will still run the dd and only abort after that. I haven't figured out a good way to deal with that except for adding an "or die ..." after every critical test.

Leave a comment

About brian d foy

user-pic I'm the author of Mastering Perl, and the co-author of Learning Perl (6th Edition), Intermediate Perl, Programming Perl (4th Edition) and Effective Perl Programming (2nd Edition).