1

Modification to Perl's @INC array seems for an individual scope very confusing. I would like some clarification, as it seems to be fighting any means of dynamic initialization of objects.

One would think that I could define it as local to solve this problem.

According to the manual, "local modifies the listed variables to be local to the enclosing block, file, or eval."

The part that is annoying me is the "or" portion.

Problem: I would like to change the @INC array to include one and ONLY one directory under certain circumstances and ONLY for the current file.

Example attempt and issues:

Lets say I have a launching script index.pl:

#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';

use File::Basename;
# Lets say I want to modify @INC here to look in ONLY one path.  Local
# should allow us to declare for one scope or file (how non explicit this
# is annoys me) Since I have not defined a scope with brackets, it should
# be effective for the current file

local @INC = (dirname(__FILE__) . '/foo/'); #some relative path

# Lets say bar now uses standard perl modules
require 'bar.pm';
# ^- This will fail because local did not work as described, fails at use
# XML::Simple because it is traversing foo

my $bar = bar->new();

For the sake of being comprehensive, here is a bar.pm:

package bar;
use strict;
use warnings;

sub new
{
    my $class = shift;
    my $self = bless {}, $class;

    use XML::Simple;
    return $self;
}
1;

Is there anyway to modify @INC ONLY for the current file while leaving it intact in all parsed files afterwards?

(I know I can unshift, but eventually there could be dozens of directories it could be traversing)

Jim Davis
  • 5,241
  • 1
  • 26
  • 22
steve
  • 290
  • 1
  • 11

3 Answers3

2
require(dirname(__FILE__) . '/foo/bar.pm');
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I was going to downvote this, as it does not solve the actual problem of modifying @INC, but this does indeed work for ~90% of what im trying to do. – steve Aug 28 '15 at 22:26
  • 1
    You asked to change `@INC` in the middle of `require`. That's rather hard to do without changing `require`. Not impossible, mind you. Replacing `@INC` with a magical variable would work. – ikegami Aug 28 '15 at 22:31
2
use File::Basename;
use subs 'require';
sub require {
    my $module_file = shift;
    die "unexpected absolute path $module_file\n" if $module_file =~ /^\//;
    CORE::require(dirname(__FILE__) . '/foo/' . $module_file);
}

See http://perldoc.perl.org/CORE.html#OVERRIDING-CORE-FUNCTIONS

ysth
  • 96,171
  • 6
  • 121
  • 214
  • Clever - this calls `main::require` from the main file. In another package inside `bar.pm`, it calls the builtin `require`. You could restrict its scope even further with `{ local *require = sub { ... } ... }` – mob Aug 28 '15 at 23:20
0

local @INC works, but your bar.pm file still needs to be able to find XML/Simple.pm (a use statement is executed at the time a file is compiled, no matter where it appears in the file), presumably from the original @INC, so your local @INC should start with a copy of the original @INC.

{
    local @INC = (dirname(__FILE__) . '/foo/', @INC);

    require 'bar.pm';
}        # local @INC out of scope now, original @INC restored

my $bar = bar->new();
mob
  • 117,087
  • 18
  • 149
  • 283
  • 1
    Nope, `@INC` is still modified when `bar.pm` is compiled. For your code to work, he'd have to change `use XML::Simple;` to `require XML::Simple;`. Messing with `@INC` and contorting the module is not nearly as good a solution as passing the correct file name to `require`, though. – ikegami Aug 28 '15 at 22:31
  • How is that different from just unshifting to @INC without declaring it local though? Essentially you're creating a copy in the current scope, which continues until it leaves the current scope. It would be redundant. – steve Aug 28 '15 at 22:32
  • @steve, The whole purpose of `local` is to make a backup of the variable and restore it on scope exit. `{ local @INC = @INC; ... }` is the same as `{ my @backup = @INC; ... @INC = @backup }` except it works if the scope is exited by an exception too. – ikegami Aug 28 '15 at 22:32
  • My question is what IS a scope exit though? for perl's manual it seems to be the enclosing brackets, OR eval, OR file. What if I want to be explicit? – steve Aug 28 '15 at 22:34
  • 1
    @steve, Generally, files and curlies bound lexical scopes. The lexical scope that contains the `local` is exited when the `}` is reached. – ikegami Aug 28 '15 at 22:35
  • @ikegami Thanks for all your help, seems that I have more reading to do – steve Aug 28 '15 at 22:36