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

Thanks for helping me! this was exactly the solution I was looking for.

Hi, is the any way to catch an error while initializing pm like "use SOME::PM" or call "use ..." only when corresponding library is installed?

I am trying to implement support of GZIP to the script. The idea is to switch compression/decompression ON or OFF with the flag. The usual way will be to initialize corresponding library in the beginning and just use it when needed:

#!/usr/bin/perl use IO::Compress::Gzip qw(gzip $GzipError); use Getopt::Long; #usage: $0 --gzip my $useGZ; my $options = &Getopt::Long::GetOptions( 'gzip|z' => \$useGZ ); my $OUT_FHs; my $out_file = "test.txt"; if($useGZ) { $OUT_FHs = new IO::Compress::Gzip ("$out_file.gz") or die $GzipError; } else { open $OUT_FHs, '>', $out_file or die "Can't open $out_file for writin +g; $!\n"; } print $OUT_FHs "printing to test file\n"; close($OUT_FHs); exit;

The problem came from usual site - on my test system everything works, but on the user's system it doesn't and exits with an error:

Can't locate IO/Compress/Gzip.pm in @INC (@INC contains: /usr/local/li +b64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/sh +are/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .)

Obviously, the module is not installed or not in PATH on the user's system. The Perl doesn't allow to go further with the execution of the script even if I am not using the compression/decompression functionality.

Unfortunately the user can't change or install anything on the system so I need to find a way around to enable GZIP support when needed but to ignore error message from module initialization if it's not installed on the system.

Replies are listed 'Best First'.
Re: error handling by pm initialization
by Discipulus (Canon) on Dec 13, 2016 at 10:28 UTC
    Hello homeveg

    Usually the check condition if a module is present is done in BEGIN block using eval to test a require instead of use because the latter is done at compiletime.

    Something like the following can be used:

    my $gzip; BEGIN{ $gzip = eval { require IO::Compress::Gzip; IO::Compress::Gzip->import(); 1; #UPDATE as per Eily's comment: this +is needed }; print "IO::Compress::Gzip found!" if $gzip; }

    Notice also that on CPAN there is Module::Load::Conditional that can be usefull... but must be installed!

    Also interesting is perlancar article

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
      Thanks for your solution and links. I've checked as well CPAN Module::Load::Conditional - looks promising. Will definitely help distributing Perl scripts to others.
Re: error handling by pm initialization
by Eily (Monsignor) on Dec 13, 2016 at 10:30 UTC

    You'll find some info on how to load a module under conditions on the if doc. Also, the use manpage tells you that use is equivalent to:  BEGIN { require Module; Module->import( LIST ); } Where require include the files, and import imports the symbols into the current namespace. You can run your require in eval to see if it succeeds or fails (for any reason):

    my $ModuleIsLoaded; BEGIN { $ModuleIsLoaded = eval "require Module; 1"; }

      Thanks for solution and links!
Re: error handling by pm initialization
by haukex (Archbishop) on Dec 13, 2016 at 10:35 UTC

    Hi homeveg,

    One way to optionally load a module is using stringy eval, which is often abused and can lead to security issues, but can be acceptable with fixed strings:

    if ($useGZ) { eval q{ use IO::Compress::Gzip; 1 } or die "IO::Compress::Gzip is NOT available: $@"; print "IO::Compress::Gzip is available\n"; }

    Note that since loading of the module is deferred to runtime, there are some side effects: the obvious one is that errors are deferred until runtime, but the possibly more subtle one is that in the case where the module is not loaded, any imports from the module are not available to the entire script, which might lead to compile-time errors. Or worse, hard to detect differences in how Perl interprets your code, for example, in the following, Perl will think that print Dumper $x; is print FILEHANDLE $x;, because it doesn't yet know about the function Dumper(), leading to the confusing warning message "print() on unopened filehandle Dumper". In general, parentheses on optionally imported functions will be required.

    eval 'use Data::Dumper; 1' or die $@; print Dumper $x; # WON'T work, confusing warning message print Dumper($x); # will work

    Also, you'll have to refer to $GzipError as $IO::Compress::Gzip::GzipError instead, since that variable is also no longer imported at compile time.

    Hope this helps,
    -- Hauke D

    Updates: Added sentence about the interpretation of code and the confusing warning message, and the note on stringy eval.

      Thanks for solution and notes. In the end, I've decided to go the way you described here.
Re: error handling by pm initialization
by marto (Cardinal) on Dec 13, 2016 at 10:48 UTC

    In addition to the existing answers, have you considered packaging your script and it's dependencies? pp/PAR etc.

      Тhanks! This is very useful link for me. I was looking for such solution from time to time but without success. Actually, some time ago I was using commercial compiler from ActiveStates, but free solution is much better now.
Re: error handling by pm initialization
by BrowserUk (Patriarch) on Dec 13, 2016 at 13:24 UTC

    See Module::Load::Conditional.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanks! Discipulus below as well suggested this module, but unfortunately the problem with installation of new modules on user computer remains.
        the problem with installation of new modules

        Have you heard of cut&paste?


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: error handling by pm initialization
by haukex (Archbishop) on Dec 15, 2016 at 10:13 UTC

    Hi homeveg,

    Just had this idea for a possible alternative solution: Since you seem to be on a *NIX system, perhaps this is a place where the UNIX philosophy could be used: yourscript.pl | gzip >out.gz

    Of course there's nothing wrong with doing the compression in your script, and it might even be necessary if, for example, your script creates multiple output files.

    Regards,
    -- Hauke D