Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Use 'use' in foreach

by zidi (Acolyte)
on Jul 19, 2017 at 10:41 UTC ( [id://1195426]=perlquestion: print w/replies, xml ) Need Help??

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

Hey, I am currently storing some non core perl modules in an array like this:
my @NotInstalledModules = ( 'Foo::Bar', 'Bar::Foo', )
And I am able to install these modules (install, if eval("use $_") not possible...). The only problem is that I can't 'use' them afterwards. I tried:
foreach (@Dependencies){ use $_; }
which is not working. Any help is appreciated Best regards

Replies are listed 'Best First'.
Re: Use 'use' in foreach
by hippo (Bishop) on Jul 19, 2017 at 10:51 UTC
    which is not working

    Blue smoke? It is likely "not working" because use is a compile-time action. With Module::Load it is trivial.

    #!/usr/bin/env perl use strict; use warnings; use Module::Load; my @mods = ('JSON', 'CGI::Lite'); for my $mod (@mods) { load $mod; print "$mod version is " . $mod->VERSION . "\n"; }

    This can also be done with require if you don't fancy using the module.

      A little bit modified, you can/should also check before loading if it is already loaded:

      for my $mod (@mods) { if(!defined($mod->VERSION)) { load $mod; print "$mod version is " . $mod->VERSION . "\n"; } }
      "For me, programming in Perl is like my cooking. The result may not always taste nice, but it's quick, painless and it get's food on the table."
        you can/should also check before loading if it is already loaded

        You can, but should you? What benefit is gleaned by doing so?

        If I simply double up the array of modules the script runs without error or warning and produces the expected results:

        #!/usr/bin/env perl use strict; use warnings; use Module::Load; my @mods = ('JSON', 'CGI::Lite'); for my $mod (@mods, @mods) { load $mod; print "$mod version is " . $mod->VERSION . "\n"; }
      Module::Load

      I was surprised to find this two-year old bug report:

      Module::Load::load is vulnerable to path traversal attacks, and this is by design (because load() can load both modules and arbitrary files) and can't be fixed.

      I haven't looked into it deeper yet, but if true, that could be a potential drawback to this module when using user-supplied strings.

        that could be a potential drawback to this module when using user-supplied strings.

        Technically, that's true. However ISTM that it's much the same as saying that DBI is vulnerable by design because putting user-supplied strings into a do() or prepare() call could result in SQL injection. But that's OK because nobody in their right mind would write code which passed unvalidated user-supplied data to such methods. And the same is true for Module::Load. Perhaps moreso because in the latter's case it is easily blocked by taint mode:

        $ cat tm.pl #!/usr/bin/perl -T use strict; use warnings; use Module::Load; my $garbage = shift @ARGV; load $garbage; print "This is fine.\n"; $ ./tm.pl foo Insecure dependency in require while running with -T switch at /usr/sh +are/perl5/vendor_perl/Module/Load.pm line 77. Insecure dependency in require while running with -T switch at /usr/sh +are/perl5/vendor_perl/Module/Load.pm line 77. $

        YMMV but I'm perfectly happy to carry on using it in a secure fashion.

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Use 'use' in foreach
by haukex (Archbishop) on Jul 19, 2017 at 10:54 UTC
    And I am able to install these modules ... The only problem is that I can't 'use' them afterwards.

    If you can install them (using one of the many ways, cpan, cpanm, perl Makefile.PL && make && make test && make install, ppm, ...) then you should be able to use them. Otherwise, you may have installed them in a different location than your perl expects, this might happen, for example, if you have two versions of Perl installed.

    Note that since use has a compile-time effect, it doesn't make sense to put it in a foreach. If you want to have the advantages of that compile-time effect, like e.g. not having to use parentheses on imported functions, then the list of modules has to be known at compile time, so it's unclear to me why you don't just write use Foo::Bar; use Bar::Foo;.

    Loading modules at runtime can be achieved in other ways. For example, require Foo::Bar; Foo::Bar->import; or my $modulename = "Foo::Bar"; eval "use $modulename; 1" or die $@; (Update before posting: Or e.g. Module::Load, as hippo showed).

    Update: If you are still having trouble, please show us an SSCCE so we can try to reproduce the problem on our end, plus the error messages you're getting. See also How do I post a question effectively?

      Thanks for the long answere. I have one array with all non core modules I need for the execution of the script. If eval "use $_" fails, the module gets pushed into the second array — @NotInstalledModules. The script then installs all @NotInstalledModules via cpanm. eval "use $_" was the last missing part of the puzzle after the installation.

        Personally, I would look at splitting the script into two scripts and using the standard Perl installation approach, but I understand if you want to make things as easy as possible for the user.

        The standard installation approach would be to list all the modules your program needs in Makefile.PL and then just use cpanm --installdeps . to install all the modules your script needs automatically.

        This has the drawback that you need a second file with your script, but the huge advantage that your installation process now also works with cpan and almost all other, and future installation processes for Perl stuff.

        A very simplicistic Makefile.PL would look like the following:

        #!perl -w use ExtUtils::MakeMaker; our %module = ( NAME => 'myscript, AUTHOR => q{zidi <zidi@example.com>}, PREREQ_PM => { 'strict' => 0, ... }, ); if(! caller()) { WriteMakefile( %module ); }; 1;
Re: Use 'use' in foreach
by shmem (Chancellor) on Jul 19, 2017 at 11:27 UTC
    The only problem is that I can't 'use' them afterwards. I tried:
    foreach (@Dependencies){ use $_; }

    use needs a bareword. Just do the same you did when testing for the modules:

    foreach (@Dependencies){ eval "use $_;1" or die "Can't load '$_'!\n"; }

    If you want to avoid string eval for some sort of paranoia (but be aware that require uses string eval under the hood), you can emulate use with require and a call to import():

    foreach my $mod (@Dependencies){ (my $file = $mod) =~ s{::}{/}g; $file .= '.pm'; require $file or die "Can't load '$mod' (file '$file')!\n"; $mod->import; }
    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      Thanks for all the answere. This one works like a charm. Funny how I used eval("use $_") and thought it would only check if it's in my lib. Little did I know it would also "use" if available. Thanks!!!
Re: Use 'use' in foreach
by 1nickt (Canon) on Jul 19, 2017 at 12:31 UTC

    Hi, I don't know how hard you searched, and the title of the question is not very descriptive, so you might have missed it, but see this thread from a week ago.


    The way forward always starts with a minimal test.
Re: Use 'use' in foreach
by ikegami (Patriarch) on Jul 21, 2017 at 03:28 UTC
    use happens at compile-time.
    BEGIN { for (@deps) { my $dep = $_; $dep =~ s{::}{/}g; $dep .= ".pm"; require $dep; } }

    You could indeed use eval, but you then have the tricky task of validating the name of the dependency first.

    BEGIN { for my $dep (@deps) { /^\w+(?:::\w+)*\z/ or die("Invalid"); eval("use $dep; 1") or die($@); } }
      You could indeed use eval, but you then have the tricky task of validating the name of the dependency first.

      I think that you should validate the name in any case. BTW: Taint mode enforces that you validate the name.

      /tmp>cat taint-require.pl #!/usr/bin/perl -T use strict; use warnings; my $mod=<STDIN>; chomp $mod; $mod.='.pm'; $mod=~s!(::|')!/!g; require $mod; print $mod->VERSION; /tmp>chmod +x taint-require.pl /tmp>echo Data::Dumper | taint-require.pl Insecure dependency in require while running with -T switch at ./taint +-require.pl line 11, <STDIN> line 1. /tmp>
      $dep =~ s{::}{/}g; $dep .= ".pm"; require $dep;

      That's strictly speaking not sufficient. ' can be used as a separator in module names in place of :: (perl4 legacy). Some fun modules, like Acme::Don't, still use this feature. And perl still accepts ' in place of :::

      /tmp>cat perl4-mod.pl #!/usr/bin/perl use strict; use warnings; use Data'Dumper; print Dumper(\%INC); /tmp>perl perl4-mod.pl $VAR1 = { 'warnings/register.pm' => '/usr/share/perl5/warnings/registe +r.pm', 'strict.pm' => '/usr/share/perl5/strict.pm', 'constant.pm' => '/usr/share/perl5/constant.pm', 'warnings.pm' => '/usr/share/perl5/warnings.pm', 'overload.pm' => '/usr/share/perl5/overload.pm', 'Exporter.pm' => '/usr/share/perl5/Exporter.pm', 'overloading.pm' => '/usr/share/perl5/overloading.pm', 'Carp.pm' => '/usr/share/perl5/Carp.pm', 'XSLoader.pm' => '/usr/local/lib64/perl5/XSLoader.pm', 'Data/Dumper.pm' => '/usr/lib64/perl5/Data/Dumper.pm', 'bytes.pm' => '/usr/share/perl5/bytes.pm' }; /tmp>perl -v This is perl 5, version 22, subversion 2 (v5.22.2) built for x86_64-li +nux-thread-multi Copyright 1987-2015, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. /tmp>

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1195426]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-03-28 23:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found