I have a Perl script which uses a not-so-common module, and I want it to be usable without that module being installed, although with limited functionality. Is it possible?
I thought of something like this:
my $has_foobar;
if (has_module "foobar") {
<< use it >>
$has_foobar = true;
} else {
print STDERR "Warning: foobar not found. Not using it.\n";
$has_foobar = false;
}
You can use require to load modules at runtime, and eval to trap possible exceptions:
eval {
require Foobar;
Foobar->import();
};
if ($#) {
warn "Error including Foobar: $#";
}
See also perldoc use.
Consider the if pragma.
use if CONDITION, MODULE => ARGUMENTS;
I recommend employing Module::Load so that the intention is made clear.
Edit: disregard the comments, Module::Load is in core.
Another approach is to use Class::MOP's load_class method. You can use it like:
Class::MOP::load_class( 'foobar', $some_options )
It throws an exception so you'll have to catch that. More info here.
Also, while this isn't necessarily on every system Class::MOP is awfully useful to have and with Moose becoming more prevalent every day it likely is on your system.
Related
I have Perl code which relies on Term::ReadKey to get the terminal width. My installation is missing this module, so I want to provide a default if the module isn't present rather than throw an exception.
How can I conditionally use an optional module, without knowing ahead of time whether it is available.
# but only if the module is installed and exists
use Term::ReadKey;
...
How can I accomplish this?
Here's a bare-bones solution that does not require another module:
my $rc = eval
{
require Term::ReadKey;
Term::ReadKey->import();
1;
};
if($rc)
{
# 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.
Check out the CPAN module Module::Load::Conditional. It will do what you want.
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.
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.
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) {
# ...
}
use Module::Load::Conditional qw(check_install);
use if check_install(module => 'Clipboard') != undef, 'Clipboard'; # class methods: paste, copy
using if pragma and Module::Load::Conditional core module.
check_install returns hashref or undef.
this module is also mentioned in the see also section of the pragma's documentation:
Module::Load::Conditional provides a number of functions you can use to query what modules are available, and then load one or more of them at runtime.
This is an effective idiom for loading an optional module (so long as you're not using it in a code base with sigdie handler),
use constant HAS_MODULE => defined eval { require Module };
This will require the module if available, and store the status in a constant.
You can use this like,
use constant HAS_READLINE => defined eval { require Term::ReadKey };
my $width = 80;
if ( HAS_READLINE ) {
$width = # ... code, override default.
}
Note, if you need to import it and bring in the symbols you can easily do that too. You can follow it up.
use constant HAS_READLINE => defined eval { require Term::ReadKey };
Term::ReadKey->import if HAS_READLINE;
This method uses constants. This has the advantage that if you don't have this module the dead code paths are purged from the optree.
I think it doesn't work when using variables.
Please check this link which explains how it can be used with variable
$class = 'Foo::Bar';
require $class; # $class is not a bareword
#or
require "Foo::Bar"; # not a bareword because of the ""
The require function will look for the "Foo::Bar" file in the #INC array and will complain about not finding "Foo::Bar" there. In this case you can do:
eval "require $class";
Moose is very lovely, but sometimes simple typos can cause hair-raisingly exciting long stacktraces with, from my point of view, zero useful content.
So, are there any tools to interpret this exploding into something helpful?
In particular for classes using plain Moose, Moose+MooseX::Method::Signatures, and MooseX::Declare.
The tools only need to be helpful while developing to catch those typo or thinko problems that make things just not work.
=========================
Following suggestion below, I'm using this not-quite-a-module-yet which is reducing my headaches a little, more ideas welcome, though:
package MooseX::QuietCarping;
# Not actually a Moose thing, but helpful for Moose.
# calm Moose-internal stacktraces down a little
use Carp;
my %retain = ();
sub import {
my $class = shift;
$retain{$_}++ for #_;
}
CHECK {
for (sort keys %INC) {
s{\.pm$}{};
s{[/\\]}{::}g; # CROSS PLATFORM MY ARSE
next if $retain{$_};
$Carp::Internal{$_}++ if /^(?:Class::MOP|Moose|MooseX)\b/
}
%retain = (); # don't need this no more
}
1;
One way I experimented with some time ago is putting Moose related classes into %Carp::Internal hash, something like this:
$Carp::Internal{$_}++ for qw{
Class::MOP
Class::MOP::Attribute
Class::MOP::Class
...
};
Such classes will be skipped in stack trace, making it more compact and emphasizing your own code.
You can find them by traversing %INC variable:
package Dummy;
use Moose;
use MooseX::Declare;
# use ....;
for (sort keys %INC) {
s{\.pm$}{};
s{/}{::}g;
print "$_\n" if /^(Class::MOP|Moose|MooseX)\b/;
}
I seem to recall seeing a PerlMonks post by stvn a week or two ago saying that they're working on improving the Moose error messages. I don't think there's anything currently available to clean the up, though.
Method::Signatures::Modifiers is a package which hopes to fix some of the problems of MooseX::Method::Signatures. Simply use it to overload.
use MooseX::Declare;
use Method::Signatures::Modifiers;
class Foo
{
method bar (Int $thing) {
# this method is declared with Method::Signatures instead of MooseX::Method::Signatures
}
}
What's the best way to programatically discover all of the subroutines a perl module has? This could be a module, a class (no #EXPORT), or anything in-between.
Edit: All of the methods below look like they will work. I'd probably use the Class::Sniff or Class::Inspector in production. However, Leon's answer is marked as 'accepted' since it answers the question as posed, even though no strict 'refs' has to be used. :-) Class::Sniff may be a good choice as it progresses; it looks like a lot of thought has gone into it.
sub list_module {
my $module = shift;
no strict 'refs';
return grep { defined &{"$module\::$_"} } keys %{"$module\::"}
}
ETA: if you want to filter out imported subroutines, you can do this
use B qw/svref_2object/;
sub in_package {
my ($coderef, $package) = #_;
my $cv = svref_2object($coderef);
return if not $cv->isa('B::CV') or $cv->GV->isa('B::SPECIAL');
return $cv->GV->STASH->NAME eq $package;
}
sub list_module {
my $module = shift;
no strict 'refs';
return grep { defined &{"$module\::$_"} and in_package(\&{*$_}, $module) } keys %{"$module\::"}
}
Class::Inspector:
Class::Inspector allows you to get information about a loaded class. Most or all of this information can be found in other ways, but they aren't always very friendly, and usually involve a relatively high level of Perl wizardry, or strange and unusual looking code. Class::Inspector attempts to provide an easier, more friendly interface to this information...
Have a look at this:
Class::Sniff
The interface is rather ad-hoc at the moment and is likely to change. After creating a new instance, calling the report method is your best option. You can then visually examine it to look for potential problems:
my $sniff = Class::Sniff->new({class => 'Some::Class'});
print $sniff->report;
This module attempts to help programmers find 'code smells' in the object-oriented code. If it reports something, it does not mean that your code is wrong. It just means that you might want to look at your code a little bit more closely to see if you have any problems.
At the present time, we assume Perl's default left-most, depth-first search order. We may alter this in the future (and there's a work-around with the paths method. More on this later)...
I've wrapped Perl's Net::SSH::Expect with a small module to reduce the boilerplate code needed to write a new configuration script for use with our HP iLO cards. While on one hand I want this wrapper to be as lean as possible, so non-programmer colleagues can use it, I also want it to be as well-written as possible.
It's used like so:
my $ilo = iLO->new(host => $host, password => $password);
$ilo->login;
$ilo->command("cd /system1");
$ilo->command("set oemhp_server_name=$system_name", 'status=0');
and this is iLO::command():
sub command {
my ($self, $cmd, $response) = #_;
$response = 'hpiLO-> ' unless defined($response);
# $self->{ssh} is a Net::SSH::Expect object
croak "Not logged in!\n" unless ($self->{ssh});
$self->{ssh}->send($cmd);
if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) {
return {
before => $self->{ssh}->before(),
match => $self->{ssh}->match(),
after => $self->{ssh}->after(),
};
} else {
carp "ERROR: '$cmd' response did not match /$response/:\n\n",
$self->{ssh}->before()),
"\n";
return undef;
}
}
I have two related queries. First, how should I deal with responses that don't match the expected response? I guess what I'm doing now is satisfactory -- by returning undef I signal something broke and my croak() will output an error (though hardly gracefully). But it feels like a code smell. If Perl had exceptions I'd raise one and let the calling code decide whether or not to ignore it/quit/print a warning, but it doesn't (well, in 5.8). Perhaps I should return some other object (iLO::response, or something) that carries an error message and the contents of $ilo->before() (which is just Net::SSH::Expect's before())? But if I do that -- and have to wrap every $ilo->command in a test to catch it -- my scripts are going to be full of boilerplate again.
Secondly, what should I return for success? Again, my hash containing more-or-less the response from Net::SSH::Expect does the job but it doesn't feel 'right' somehow. Although this example's in Perl my code in other languages emits the same familiar smell: I'm never sure how or what to return from a method. What can you tell me?
If you're familiar with exceptions from languages like Java, then think of die as throw and eval as try and catch. Instead of returning undef, you could do something like this:
if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) {
return {
before => $self->{ssh}->before(),
match => $self->{ssh}->match(),
after => $self->{ssh}->after(),
};
}
die "ERROR: '$cmd' response did not match /$response/:\n\n"
. $self->{ssh}->before();
Then, in your calling code:
eval {
$ilo->command("set oemhp_server_name=$system_name", 'status=0');
};
if ( my $error = $# ) {
# handle $error here
}
Just like exceptions in other languages, this allows you to bail out of a submethod at any point without having to worry about propagating return values up the call stack. They'll be caught by the first eval block that finds them. Additionally, you can die again to rethrow an exception you can't deal with back up the stack.
Even better, you can use die to throw an object, which your exception handler can interrogate for useful information and error messages. I like to use Exception::Class for this purpose. The Error module provides some syntactic sugar for doing Java-like try/catch blocks, as well.
The usual way to raise exceptions in Perl is with die. The usual way to catch them is using eval with a block as an argument, and testing $# after the eval finishes.
You'll find a lot of discussion of this sort of thing in googlespace. The best practice, no matter what you decide, is to not overload any of the values so the return value means different things. It should always be an error code, or it should never be the error code. People shouldn't have to look at the actual value to decide if it is an error code or not.
Check out the popular Perl modules on CPAN (or the ones you already use) to see what they do. I even talk about this a little in Mastering Perl, but I don't give a black-and-white answer. As with all real code, the real answer is "It depends".
There are a lot of different ways to do this. Unfortunately, that means people do it in every way. Since that is the case, I invoke consistency as the paramount rule. What does most of the code already do (not counting the wrong way)? If I have to fit into an existing codebase, I try to use the same sort of interface that most of the code already uses.
If there is no clear winner, write use cases using a couple of different styles. Which one fits the problem better or more naturally expresses the steps most users will take? That's not always just reading for die and eval. Write sample scripts using your unimplemented interfaces. Which style are you going to want to use? I found that actually writing the script before I implement the interface shows me a lot more than I was thinking about. If I'm writing stuff for other people to use, I show them the scripts in different styles and ask them which one they like better.
And, if all of that fails, reach for the 2d6. :)
In addition to using "die" as exception, you can also add another method:
if (!$ilo->commandSucceeded("set oemhp_server_name=$system_name", 'status=0')) {
#recover here
}
Of course, the internal implementation of command() becomes
die ... if !commandSucceeded;
I have Perl code which relies on Term::ReadKey to get the terminal width. My installation is missing this module, so I want to provide a default if the module isn't present rather than throw an exception.
How can I conditionally use an optional module, without knowing ahead of time whether it is available.
# but only if the module is installed and exists
use Term::ReadKey;
...
How can I accomplish this?
Here's a bare-bones solution that does not require another module:
my $rc = eval
{
require Term::ReadKey;
Term::ReadKey->import();
1;
};
if($rc)
{
# 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.
Check out the CPAN module Module::Load::Conditional. It will do what you want.
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.
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.
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) {
# ...
}
use Module::Load::Conditional qw(check_install);
use if check_install(module => 'Clipboard') != undef, 'Clipboard'; # class methods: paste, copy
using if pragma and Module::Load::Conditional core module.
check_install returns hashref or undef.
this module is also mentioned in the see also section of the pragma's documentation:
Module::Load::Conditional provides a number of functions you can use to query what modules are available, and then load one or more of them at runtime.
This is an effective idiom for loading an optional module (so long as you're not using it in a code base with sigdie handler),
use constant HAS_MODULE => defined eval { require Module };
This will require the module if available, and store the status in a constant.
You can use this like,
use constant HAS_READLINE => defined eval { require Term::ReadKey };
my $width = 80;
if ( HAS_READLINE ) {
$width = # ... code, override default.
}
Note, if you need to import it and bring in the symbols you can easily do that too. You can follow it up.
use constant HAS_READLINE => defined eval { require Term::ReadKey };
Term::ReadKey->import if HAS_READLINE;
This method uses constants. This has the advantage that if you don't have this module the dead code paths are purged from the optree.
I think it doesn't work when using variables.
Please check this link which explains how it can be used with variable
$class = 'Foo::Bar';
require $class; # $class is not a bareword
#or
require "Foo::Bar"; # not a bareword because of the ""
The require function will look for the "Foo::Bar" file in the #INC array and will complain about not finding "Foo::Bar" there. In this case you can do:
eval "require $class";