Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Testing for a module's presence

by Anonymous Monk
on Feb 06, 2005 at 02:27 UTC ( #428412=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Usually when I want to see if a module is present, I do this from the command-line:
perl -e "use Some::Module";
But now I need to test if a module is present from my program. I don't need to use the module. I just want to know if it's there. Traversing @INC is out of the question. And I don't want to put something in a BEGIN block. In my program, sometimes I want to test if the module is there, and other times I don't test for its presence.

My thought is to put something in a do or eval. What would be the best way to handle this task?

Comment on Testing for a module's presence
Download Code
Re: Testing for a module's presence
by jpk236 (Monk) on Feb 06, 2005 at 02:42 UTC
    if you're running FreeBSD, or a similar operating system, you could do something as follows:
    #>pkg_info | grep -i "MODULE"
    Justin
      This assumes that you only install modules using your OSes packaging system.
        Why wouldn't you want to? That's the beauty of FreeBSD.

        Justin
Re: Testing for a module's presence
by rlb3 (Deacon) on Feb 06, 2005 at 02:44 UTC
    Hello,
    I think you would have to do something like:
    eval { require Some::Module; }; if ($@) { print "Can't find Some::Module\n"; }

    When you say "use", things happen at compile time and would happen before the eval so, the error would not get trapped.

    rlb3
      "require" also better because it doesn't call import(). The module code still gets executed though. Typically, "require"d modules just create subs in their own package, but a badly-behaved module could in principle mess up any namespace.

      If you want to "use" a module at runtime, you can eval a quoted string:
      eval "use $mymodule";<br>print $@ if $@;

Re: Testing for a module's presence
by dimar (Curate) on Feb 06, 2005 at 03:21 UTC
      I used super search and googled thepen. The problem with such a search is the keywords: I used "test" and not "check," and "presence" or "exists" and not "installed".

      Perhaps the problem is twofold: Perlmonks doesn't group questions in SOPW. Everything is thrown into one big fat section. This makes searching more difficult. The other problem is the English language. There are so many synonyms like "test" and "check".

        I used super search and googled thepen. The problem with such a search is the keywords: I used "test" and not "check," and "presence" or "exists" and not "installed".

        ... which is why I said "*If* you haven't already" tried supersearch ... just like you, I noticed the keyword disparity and I did not automatically assume you had overlooked supersearch.

        As far as your observations on SOPW organization and English synonyms, my initial response to you was specifically intended to address those (percieved) problems.

        How?

        Well you see, now there is a new node that contains *all* of the different keywords in one place. It patches the 'keyword gap'. That's how humans like you and me can help make SuperSearch and Google even better.

        Of course there are many other synonyms out there that remain "unconnected", but that's the name of the game ...

        Wash, Rinse, Repeat.

Re: Testing for a module's presence
by sgifford (Prior) on Feb 06, 2005 at 04:04 UTC
    If you don't want to affect your running program at all and speed isn't critical, just fire up a new copy of perl:
    #!/usr/bin/perl use warnings; use strict; use POSIX qw(_exit); foreach my $mod (qw(CPAN No::Such IO::Socket Not::Installed)) { printf "%20s: %-3s\n",$mod,is_module_installed($mod)?"Yes":"No"; }
    sub is_module_installed { my($mod)=@_; return (system("perl -M$mod -e 1 2>/dev/null") == 0); }
    sub is_module_installed { my($mod)=@_; defined(my $pid = fork) or die "Fork error: $!\n"; if ($pid) { # Parent waitpid $pid,0; return $? == 0; } else { # Child close(STDERR); eval "use $mod;"; _exit($@?1:0); } }

    You could make it a little faster usingfork, to avoid firing up a new interpreter from scratch.

    Updated: Tanktalus points out that the copy of perl that system('perl ...') will start may not be the same as the running one; fork will use the currently running interpreter in a new process, so avoids that problem. It should also be a bit faster.

    Updated: Fixed Tanktalus' name in previous update. :) Also addressed his concerns about atexit using POSIX::_exit.

      We take some risks here either way. "perl" may not be the currently running perl (here, for example, "perl" is the stock perl that comes with the Linux distro I'm running - 5.8.0, while "perl5.8" is a symlink to the latest perl5.8 binary I've compiled - 5.8.5), while using $^X may also not be quite useful (since the currently executing code may be running in an embedded perl rather than a standalone perl executable - isn't that how mod_perl works?).

      Personally, I'd just use require as others have pointed out. And if you don't want to use the extra memory, you can delete it from %INC afterwards - perl will then be able to re-use that memory. It does mean that you'll get a negative when the module exists and is found, but doesn't compile, but that's probably the same thing as not being there, really.

        And if you don't want to use the extra memory, you can delete it from %INC afterwards - perl will then be able to re-use that memory.

        Memory is an issue! Can you show me how to delete it from %INC after I required the module in an eval? Thanks.

        Will deleting from %INC really free much memory? For example, will it free storage space for strings and subs created by the module when it's required? Or does it just free the storage for the filename itself?

      (Ignoring the typo of missing the 'n' in my alias ;->)

      While fork can work great on Unix, it's not quite so great on other platforms. EMX on OS/2 (and maybe Win32) handles fork - but it's not really faster. I doubt ActiveState or Cygwin perls handle it (if either do, it'll be Cygwin). So you've simply hit another gotcha: cross-platform compatability. (Traversing @INC and using File::Spec is fast, tight on memory, and completely cross-platform!)

      Even on Unix, fork may not be that great. Imagine an embedded perl. The main process (the embedder) has some cleanup in an atexit() in C. That cleanup may include committing or rolling back transactions, deleting temporary files that were in use, or other such behaviour. When you fork, and then the child exits, the atexit handler kicks in and does something like this - and now the parent process is going to be in a wierd place that is going to be really painful to debug. Especially if this ends up on a user's machine and the user imported your module. The C developer has no idea what is causing it, the perl user has no idea, and you're not really involved. Dangerous!

      Fork is a dangerous tool - although it can be useful, you have to be really really careful of when you use it, and how you use it. Let's just stick to searching @INC. It's fewer lines of code, too ;-}

        The thing I don't like about searching @INC is that it's re-implementing a part of Perl based on how it works right now. A future version of Perl could use a different scheme for locating libraries. For example, it could add a new type of arch directory, hash the directories to avoid too many modules in the same directory, add a new .pmz type for compressed modules on disk-poor systems, or load code across the network. A user could even provide their own copy of the lib module and/or the require function to implement those sorts of things right now.

        In the face of possible changes to the module search algorithm, actually asking Perl to find the library and say whether it worked or not will always work, while re-implementing the library search yourself will only work for as long as nothing changes.

        Using a module designed to do this, as others have suggested here, is a good compromise.

        As for the cross-platform concerns, I have used fork on ActiveState Windows; it seems like a lot would break without a working copy of this function. And POSIX::_exit seems to avoid your other concerns.

        Also, to clarify, I don't think using fork will be particularly fast; just faster than using system, which was my original proposal.

        As my sister comment mentions, ActiveState has some support for fork(). In my experience, only a few obscure things don't work.

        But:

        % perldoc perlfork ... AUTHOR Support for concurrent interpreters and the fork() emulation wa +s implemented by ActiveState, with funding from Microsoft Corporation +. This document is authored and maintained by Gurusamy Sarathy <. +.@activestate.com>. ...
Re: Testing for a module's presence
by broquaint (Abbot) on Feb 06, 2005 at 04:36 UTC
    You could use Module::Locate for the job e.g
    use Module::Locate 'locate'; print "Couldn't find Some::Module" unless locate 'Some::Module';
    This code will return the path to the module if it is on the system or false if not. Also it doesn't load the module it just checks for its presence in the same manner that perl's require does.
    HTH

    _________
    broquaint

Re: Testing for a module's presence
by itub (Priest) on Feb 06, 2005 at 04:43 UTC
    I'm curious, why is traversing @INC out of the question? This would be done implicitly anyway regardless of the method you choose, whether by perl itself, a third-party module, or your own code.
Re: Testing for a module's presence
by vek (Prior) on Feb 06, 2005 at 06:07 UTC

    I usually do something like this early on in my code:

    my $HAVE_MODULE = eval 'require Some::Module; 1' ? 1 : 0;
    I can then react accordingly later on:
    if ($HAVE_MODULE) { do_stuff(); } else { do_something_else(); }

    -- vek --
Re: Testing for a module's presence
by DrHyde (Prior) on Feb 07, 2005 at 10:52 UTC
    If you just want to know whether it exists you have little choice but to grovel over @INC.

    If you try to eval "use Some::Module" then the module will be loaded and any side-effects it has (such as exporting) will happen.

    I suppose if you were feeling particularly crazy you could try something like this ...

    print 'the module exists' if(!system("perl -MSome::Module -e exit"));
    but if I saw code like that in production I'm afraid I'd have to shoot you.
Re: Testing for a module's presence
by ehdonhon (Curate) on Feb 07, 2005 at 19:28 UTC

    One way to find out if and where a module is installed, assuming it is a CPAN module, and not something you have written yourself is to use CPAN.pm:

    perl -MCPAN -MData::Dumper -e 'print Dumper( CPAN::Shell->expand( "Module", "Test::More" )->inst_file )'

    That will return the file location of the module if it is installed, and nothing if it is not.

    I note that in your question you specifically mention you do not want to use the module. However, you might want to find a way around that requirement. The problem is that just testing for existence of the .pm file is not always sufficient to know if everything has been installed. It is also not sufficient if you need to know that the file compiles clean.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://428412]
Approved by shenme
Front-paged by diotalevi
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (11)
As of 2014-08-29 10:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (278 votes), past polls