Martin Atkins (mart) wrote in perl,
Martin Atkins

@INC hooks and tied filehandles

I'm currently working on an app where I've installed a CODE ref in @INC to hook the loading of certain modules. I can't seem to find any documentation on hooking @INC, so I'm just working from examples I've found. I've seen this work with normal filesystem handles:

use Symbol;
use lib sub {
	my $sym = gensym();
	open ($sym, '<', '/dev/null');
	return $sym;

The above will fail with the error that the included module did not return a true value, since of course /dev/null doesn't end with 1;.

What I'd like to be able to do is return a string containing source code. To do this, I attempted to use IO::String to create a tied handle that operates on a string in memory:

use IO::String;
use lib sub {
	warn $_[1];
	return IO::String->new("");

In this case, Perl just ignores my result and moves on to the next entry in @INC. Is there any way to make this work, or will hooking @INC only work on real system filehandles? (If it makes any difference, I'm running these on Perl 5.8.8.)

Partial Solution: After some searching about I found the docs for this nestled at the end of the require documentation. It seems that the hook sub can return a second argument which is another coderef that's called repeatedly to fetch lines of the source file. Rather bizarrely, it must set $_ to be the result and return 1, or return 0 to signal that there are no more lines. Even more bizarrely, this only works if the hook sub returns some kind of globref as its first return value. I just made a sub that returns the string on the first run and indicates the end on the second run, like this:

my $sym = Symbol::gensym();
my $done = 0;
return $sym, sub {
    if (! $done) {
        $_ = "die 'Blah'";
        return 1;
    else {
        return 0;

Quite why you need that random glob I'm not sure; the require documentation implies that it's optional. Perhaps someone else can shed some light.

  • Post a new comment


    default userpic
  • 1 comment