Checking if a module is installed (without actually loading it)

One of the easiest ways to check if a module is installed is simply by trying to load it:

if (eval { require Foo::Bar; 1 }) {
    # Foo::Bar is loadable
}

However, when Foo::Bar happens to be installed, this actually loads the module. Which is not always desirable, for example in the cases of: 1) checking a lot of modules; 2) checking a module which is OS-specific and might not work under your OS when loaded; 3) checking a module which might conflict with another module that is already loaded; 4) wanting to avoid the security implication of executing the module’s code.

Another way to check is by trying to locate the module file by iterating over @INC yourself or using something like Module::Path or Module::Path::More. Those modules search for the module in directories specified in @INC like Perl’s require would:

use Module::Path qw(module_path);
if (module_path "Foo::Bar") {
    # Foo::Bar is available
}

However, this only works when Foo::Bar is indeed located on the filesystem and does not work when Foo::Bar is loaded using a require hook (coderef or object in @INC), like in a fatpacked or datapacked script. Also, it does not work nicely with other uses of require hooks, like emulating a missing module (lib::filter or lib::disallow).

Perl core module Module::Load::Conditional provides check_install which can handle both the cases of the module file is on the filesystem or the module is retrieved from the require hook:

use Module::Load::Conditional qw(check_install);
if (check_install(module => "Foo::Bar")) {
    # Foo::bar is available
}

In addition to the above, check_install can also be instructed to check for minimum required version:

unless (check_install(module => "Foo::Bar", version=>"1.23")) {
    # Foo::Bar is not available, or its version is < 1.23
}

Note that checking version number is not performed by loading the module and reading its $VERSION, but instead by using Module::Metadata which tries to extract the version number from the module’s source code (which might fail on some weird module that obfuscate its $VERSION, but for normal cases should suffice).

I also recently wrote Module::Installed::Tiny which does the same as Module::Load::Conditional‘s check_install but with a bit less code and dependency:

use Module::Installed::Tiny qw(module_installed);
if (module_installed "Foo::Bar") {
    # Foo::Bar is available
}

Note that check_install nor module_installed does not guarantee that the module will be loaded successfully, as there might be syntax errors in the module’s code or runtime errors when running the code. All the routines do is check that the module’s source code is available.

UPDATE [2016-08-03]: This post is originally about Module::Loadable before I was made aware of Module::Load::Conditional‘s check_install. In the original post I wrote that I hoped I didn’t reinvent the wheel by writing Module::Loadable. I was happily proven wrong đŸ™‚

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s