Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Ovid (2709)

  (email not shown publicly)
AOL IM: ovidperl (Add Buddy, Send Message)

Stuff with the Perl Foundation. A couple of patches in the Perl core. A few CPAN modules. That about sums it up.

Journal of Ovid (2709)

Monday February 16, 2009
05:16 AM

Bug in Perl Causes a Small Class::Sniff Issue

[ #38489 ]

Jesse Vincent sent a bug report about Class::Sniff detecting non-existent packages.

Seems Jesse has a lot of code like this:

eval "require RT::Ticket_Overlay";
if ($@ && $@ !~ qr{^Can't locate RT/}) {
    die $@;

Well, that seems quite reasonable. Except for this:

#!/usr/bin/env perl -l

use strict;
use warnings;

print $::{'Foo::'} || 'Not found';
eval "use Foo";
print $::{'Foo::'} || 'Not found';
eval "require Foo";
print $::{'Foo::'} || 'Not found';
Not found
Not found

That's right. Attempting the require a non-existent module via a string eval creates a symbol-table entry. Aristotle told me he was astonished that no one had caught this before. Frankly, I just think that not enough people are trying to do introspection in Perl.

This one will be tricky to work around. I thought "if the module doesn't actually exist, can I check to see if @ISA is there?" It gets automatically created for every package, but since the module representing that package doesn't exist, maybe it won't? No such luck:

print defined *NoSuchModule::ISA{ARRAY} ? 'Yes' : 'No';
print defined *NoSuchModule::xxx{ARRAY} ? 'Yes' : 'No';

That always prints "Yes" and then "No". @ISA is always created for every package if you try to access it. Darn.

I thought I could check for the module's existence in %INC, but inlined packages don't show up there, either (unless the author explicitly puts them there).

The only thing I can think of is this curious line:

print scalar keys %Foo::;

If you do that with a non-existent package which nonetheless has a symbol table entry, it still has no keys in its symbol table. However, if you do that with a module which exists but failed to load, you will probably have a few symbol table entries. This still doesn't quite solve the problem.

So how do I detect if a module in a symbol table failed to load? I'm not sure if I can. If I simply check to see if there are any keys in the symbol table, that should be enough, right? If someone evals "require $badmodule" and that require fails due to compilation errors, they'll exit or die, right? (too optimistic, I know)

Update: Alias, I've not forgotten about your request for ambiguous method detection. I've just not added it yet :)

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
More | Login | Reply
Loading... please wait.
  • I tripped on this a couple years ago. It wasn't a show-stopper and I wasn't sure it was actually a bug at the time.
  • Actually, the line

    print scalar keys %Foo::;

    will autovivify the symbol table, so if you use that anywhere in your test program, it will print "*main::Foo::" all three times.

  • This is another reason why is deprecated. It has to solve the same problem and there's no way to know if a class is partial.

    You'll also see this if the class exists but it failed during loading. You can still find the parts taht succeeded in the symbol table but you'll just be missing "the rest" and you don't have any way to know how much else "the rest" is.

    • Yeah, partial compilation has bitten me hard at times. Very unfortunate. I wonder if it's useful to have a module which attempts to clean up after bad loads?

      • Snapshots the symbol table and reverts it on failure? I wish git worked /inside/ perl.