tags:

views:

1990

answers:

6

I have some pretty simple perl code which relies on Term::ReadKey to get the terminal width; My NetBSD build is missing this module, and I want to default to 80 when the module is missing.

The odd thing is, I can't figure out how to conditionally use a module, knowing ahead of time whether it is present. This code would just quit saying it can't find Term::ReadKey if it actually can't.

#/usr/pkg/bin/perl -w
# Try loading Term::ReadKey
use Term::ReadKey;
my ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();}
my @p=(2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97);
my $plen=$#p+1;
printf("num |".("%".int(($wchar-5)/$plen)."d") x $plen."\n",@p);

I'm using Perl 5.8.7 on NetBSD and 5.8.8 on CygWin

+5  A: 

Check out the CPAN module Module::Load::Conditional. It will do what you want.

m0j0
Of course, that only works if you have that one installed, too. Probably a better solution if you do.
tvanfosson
Yeah... I can't guarantee I have that one. And while Detect::Module is, it doesn't list all the modules in it's $installed->modules() returned list of module names.
dlamblin
A: 
my $cols;
eval { use Term::ReadKey; }
if ($@) {
  $cols = 80;
} else {
  # stuff
}
mikegrb
actually that exits when it can't locate the module.
dlamblin
Look at the first response for a better way.# The correct way to do this:my $cols;eval "use Term::Readkey; };if($@){ $cols = 80;} else { # Stuff.}
gdey
+26  A: 

Here's a bare-bones solution that does not require another module:

eval
{
  require Term::ReadKey;
  Term::ReadKey->import();
};

unless($@)
{
  # Term::ReadKey loaded and imported successfully
  ...
}

Note that all the answers below (I hope they're below this one! :-) that use eval { use SomeModule } are wrong because use statements are evaluated at compile time, regardless of where in the code they appear. So if SomeModule is not available, the script will die immediately upon compiling.

(A string eval of a use statement will also work (eval 'use SomeModule';), but there's no sense parsing and compiling new code at runtime when the require/import pair does the same thing, and is syntax-checked at compile time to boot.)

Finally, note that my use of eval { ... } and $@ here is succinct for the purpose of this example. In real code, you should use something like Try::Tiny, or at least be aware of the issues it addresses.

John Siracusa
D'oh, I should have thought of that first. +1
ephemient
Yes that does really does work, and the semi-colon after the eval block is very important.
dlamblin
Avoid relying on $@ as much as possible. F.ex., some modules could set $@ as a side effect while being loaded without actually throwing an exception. The better option is to rely on the fact that `eval` will return undef when an exception was caught, ie. `if ( eval "use Term::ReadKey" ) { ... }`.
Aristotle Pagaltzis
Are you sure about that? perldoc -f eval says "If there was no error, $@ is guaranteed to be a null string." I just tried it the docs seem correct.
John Siracusa
If the module (or anything else) executes an eval after it generates an exception (such as, in a SIGDIE handler or in a DESTROY method), the value of $@ is replaced with the result of the latest eval.
daotoad
+1  A: 

The classic answer (dating back to Perl 4, at least, long before there was a 'use') was to 'require()' a module. This is executed as the script is run, rather than when compiled, and you can test for success or failure and react appropriately.

Jonathan Leffler
A: 

And if you require a specific version of the module:

my $GOT_READKEY;
BEGIN {
    eval {
        require Term::ReadKey;
        Term::ReadKey->import();
        $GOT_READKEY = 1 if $Term::ReadKey::VERSION >= 2.30;
    };
}


# elsewhere in the code
if ($GOT_READKEY) {
    # ...
}
Hinrik
A: 

if (eval {require Term::ReadKey;1;} ne 1) {

if module can't load

} else { Term::ReadKey->import(); }

or

if (eval {require Term::ReadKey;1;}) {

module loaded

Term::ReadKey->import(); }

note: the 1; only executes if require Term::... loaded properly.