Reputation: 75679
Say I have a Perl module:
use Foo::Bar;
sub add {
return Foo::Bar::func() + Foo::Buzz::func();
}
There is an error in this module, because it forgets to use Foo::Buzz
. This error will not always be catched by my unit tests, for example if the test for Foo::Buzz
runs earlier and imports Foo::Buzz
before add()
is run. If I use this module in production code, it will fail with the error that Foo::Buzz
is not imported.
How can I check whether all modules that I use in the code are also imported?
Edit: I want to check the code before deploying it in production, to avoid that any errors occur. The example will fail in production, and I want to catch the error before that, for example when I run my unit tests. I want a tool or some code that I can run before deployment that catches this error, like flake8 for python.
Upvotes: 3
Views: 745
Reputation: 107090
If Perl was strictly a dynamic language, you could easily check whether or not a module is installed in the program. The problem is that Perl isn't 100% dynamic. It does some compilation and part of that compilation work is done after modules are checked.
Bulrush is on the right track. Unfortunately, you can use use
clause in order to do this. use
is checked pre-compile, so you'll get an error before your eval
executes.
However, there's a clue in the use perldoc page:
- use Module LIST
- use Module
- use VERSION
Imports some semantics into the current package from the named module, generally by aliasing certain subroutine or variable names into your package. It is exactly equivalent to
BEGIN { require Module; Module->import( LIST ); }
There you go! You can use require
inside a BEGIN clause which is executed even before the rest of the file is parsed. You can use your eval
there to see if this works or not too. You need to use a package variable as a flag to see whether or not this worked because of the scope issues. A regular lexically scoped variable will disappear when you leave the BEGIN
clause.
BEGIN {
our $FOO_BAR_available = 0; # Must be a package variable
eval {
require Foo::Bar;
Module->import( qw(...) ); # No need if you don't import any subroutines
};
if (not $@ ) {
$FOO_BAR_AVAILABLE = 0;
}
}
Then in your program, you'd have:
our $FOO_BAR_available;
if ( not $FOO_BAR_available ) {
# Here be dragons...
}
else {
# Back to your normal code...
}
The our $FOO_BAR_available
is a bit confusing. You're not declaring this variable again, you're merely stating that you want to use this variable without prefixing it with the full package name. The variable was set in the BEGIN
clause, and this won't affect the value.
You can skip the use of a package variable entirely, if this module was written correctly. Modules are suppose to set a package variable called $VERSION
. You can use this variable as your flag:
BEGIN {
eval {
require Foo::Bar;
Module->import( qw(...) ); # No need if you don't import any subroutines
};
}
Note I not only don't have to declare a package variable, I don't even have to verify if the eval
statement worked or not.
Then in your program...
if ( not $FOO::BAR::VERSION ) {
# Here be dragons...
}
else {
# Back to your normal code...
}
If the module set the $VERSION
variable, you know it loaded. Otherwise, you know the module was not loaded.
I want to check the code before deploying it in production, to avoid that any errors occur. The example will fail in production, and I want to catch the error before that, for example when I run my unit tests.
Here's my recommendations. It isn't as simple as running a script, but it's much better:
Foo::Bar
is good, but I shouldn't use Far::Bu
because production doesn't have that. It's the first step. I'm surprised at the number of places that have no idea what's on their production environment.Use Jenkins. Jenkins is a continuous build engine. Yes, you don't compile Perl, but you can still benefit from Jenkins:
You don't normally run flake8
in a production environment. By then, it's a wee bit late.
Perl has a lot of nice tools that perform a similar function:
lint
program in C and can catch coding issues.However, this is stuff to do before you're all set to run in Production. Use Vagrant to help developers setup their own private production environment for testing. Use Jenkins to make sure you test in a very production like environment and catch errors as soon as they happen rather than after UAT testing.
Upvotes: 0
Reputation: 2363
The short answer is you can't. Since Perl is a dynamic language, you can't check whether you load all modules before runtime as you can't check whether there are some other bugs in your code.
You still can use some static code analysis, trying to find This::Pattern
in files where use This::Pattern;
is not presented, but it doesn't guarantee anything.
Upvotes: 2