lgp171188 has asked for the wisdom of the Perl Monks concerning the following question:

I'm new to Perl and I am working on a codebase where modules have a lot of use lib statements adding non-standard directories to @INC.

I want to include a directory containing patched libraries to be used by scripts (a few of which also run as services) so that it is possible to debug issues, if any, without having to modify the deployed code (the process to get write access to modify them is a cumbersome one). So I tried adding use lib <path to the patched libraries directory> to the beginning of the script. But that did not work because the imports after the use lib statements modified @INC and the patched libraries directory was no longer at the beginning of @INC.

Is there some way to restrict the scope of the use lib statements to the namespace of the module in which they were used? I cannot go and fix all the modules to no do use lib since I do not own most of the components that have those modules. If my approach to tackle this problem is wrong, is there some better way to deal with it?

Replies are listed 'Best First'.
Re: Containing 'use lib' statements in modules to their own namespace
by shmem (Chancellor) on Oct 19, 2015 at 11:26 UTC

    You can place a copy of your perl's lib.pm into the directory where your debug versions of modules live. Then edit that lib.pm to remember the first path actually passed to use lib:

    my $sticky_dir; # EDIT sub import { shift; my %names; foreach (reverse @_) { my $path = $_; # we'll be modifying it, so break the +alias $sticky_dir = $path unless $sticky_dir; # EDIT if ($path eq '') { ... # remove trailing duplicates @INC = grep { ++$names{$_} == 1 } @INC; # put sticky dir up front # EDIT @INC = ($sticky_dir, grep { ! m{^$sticky_dir$} } @INC); # EDIT return; }

    See the lines marked with # EDIT. Make sure your directory is the first which gets into the private $sticky_dir variable. All subsequent calls to use lib will result in the directories being placed after the sticky directory:

    #!/usr/bin/perl BEGIN{ # necessary unshift @INC,'/home/shmem/tmp/foo'; # so 'use lib' } # gets my version use lib "/home/shmem/tmp/foo"; # this dir is now sticky BEGIN { print "lib is ",$INC{"lib.pm"},$/; } use lib "/some/path"; BEGIN { print "\@INC after 'use lib \"/some/path\":\n"; print "$_\n" for @INC; } use lib "/another/path"; BEGIN { print "\@INC after 'use lib \"/another/path\":\n"; print "$_\n" for @INC; } __END__ lib is /home/shmem/tmp/foo/lib.pm @INC after 'use lib "/some/path": /home/shmem/tmp/foo /some/path /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl . @INC after 'use lib "/another/path": /home/shmem/tmp/foo /another/path /some/path /etc/perl /usr/local/lib/perl/5.18.2 /usr/local/share/perl/5.18.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.18 /usr/share/perl/5.18 /usr/local/lib/site_perl .

    update: Instead of the first BEGIN block, invoking a script like this

    perl -I/my/debug/directory script

    prepends /my/debug/directory to @INC to have /my/debug/directory/lib.pm loaded at the first call to use lib inside the script - so you can switch on debugging without modifying the script at all.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      I didn't understand your solution after my first few scans of your answer (possibly because I'm new to perl). But once I read it properly and thoroughly, it totally made sense and it works as well! Thanks a ton!
        Thanks a ton!

        You're welcome. See my update on how to switch on debugging without modifying your scripts at all.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Containing 'use lib' statements in modules to their own namespace
by Anonymous Monk on Oct 19, 2015 at 07:31 UTC

    Is there some way to restrict the scope of the use lib statements to the namespace of the module in which they were used?

    Not really

    if you have modules you want to override, load them before you load the others, if they use use, they won't be loaded twice

      Thanks for your reply!

      What I am trying to achieve here is to have a patched modules directory that could contain the patched versions of modules that are there in the deployed code. If there is a module in the patched library directory, it should be picked up over the one in the deployed code and if not, things should work fine with just the deployed code.

      So I am not sure how to load them before loading the others since the module names are the same and only the locations differ. Also if I load them using use, I guess I will have to run use lib before every use statement. :-/

Re: Containing 'use lib' statements in modules to their own namespace
by thargas (Deacon) on Oct 20, 2015 at 11:47 UTC

    TL;DR: only modify @INC in the main script, not in modules.

    IMHO *modules* which modify @INC, either directly or via something like use lib ... are *bad*. They're bad enough when they're part of code you control (at least you'll know when you change them), but when they're part of someone else's code, they can cause all kinds of problems which can be very difficult to track down.

    Imagine that I'm using release 3.0.0 of Some::Module which I've included in my application's lib dir. Now imagine that some other module I'm forced to use modifies @INC so that release 4.0.0 of Some::Module is now in @INC ahead of my applications lib dir. Now imagine that release 4.0.0 is incompatible with release 3.0.0, but not in an obvious crash-your-program-now way, but something subtler.

    I don't permit this kind of thing where I have any control of the code base and I've written code similar in effect to shmem's suggestion to prevent such badly behaved code from changing @INC.