|Think about Loose Coupling|
How Can I Know If a Package is "Real"?by Ovid (Cardinal)
|on Feb 16, 2009 at 12:00 UTC||Need Help??|
Ovid has asked for the
wisdom of the Perl Monks concerning the following question:
I'm working on a module named Class::Sniff. While it's primarily for finding "code smells" in object-oriented hierarchies, it also lets you graph those hierarchies. For example, here's the graph of the B:: modules (Perl's backend modules).
The problem I'm trying to solve is that requiring a package via string eval creates a symbol table entry for that package, even if the require fails. Thus, I can't tell if the package is "real" or not (e.g., if it's likely to get called and should be added to my graph).
Jesse Vincent sent a bug report about Class::Sniff detecting non-existent packages.
Seems Jesse has a lot of code like this:
Well, that seems quite reasonable. Except for this:
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:
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 compile, 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)
Of course, even this is problematic. As Rafael Garcia-Suarez pointed out, a nested stash will create its parent stash (e.g., CGI::Application will create a symbol table for CGI, even if the latter is not loaded).
I can't just check %INC because inlined modules won't be there unless the author remembers to add them manually. Could I hook require or add a coderef to @INC? It's a strange edge case I'm dealing with, but for larger, more complex applications, it's a problem.