Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re: Testing for a module's presence

by sgifford (Prior)
on Feb 06, 2005 at 04:04 UTC ( [id://428418]=note: print w/replies, xml ) Need Help??


in reply to Testing for a module's presence

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.

Replies are listed 'Best First'.
Re^2: Testing for a module's presence
by Tanktalus (Canon) on Feb 06, 2005 at 05:26 UTC

    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.

      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?

        You're probably right ... which is why a later response pointed out removing some of the global symbol table. I'm hoping someone who knows the guts of perl better can help out there, but meanwhile, searching @INC is definitely safer and faster and uses less memory :-)

      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.

        If memory is an issue, traversing @INC may be your best option. Anyway, to answer the question - assuming you're dealing with Some::Module, you would simply delete $INC{'Some/Module.pm'} although you may also need to delete it from the overall symbol table: undef ${Some::Module::} - that last part is untested, and I'm sure someone will correct me with the right way to do that. If it's possible. Which is why I go back to traversing @INC as your most memory-conscious choice, although because you won't be compiling it, its mere presence, even if it doesn't compile, will give you a different answer (probably not important, but I like to keep all the risks, however small, on the table, in case they are not small to you).

Re^2: Testing for a module's presence
by Tanktalus (Canon) on Feb 07, 2005 at 02:00 UTC

    (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.

        Or maybe add support for code refs in @INC... ;-)

        I'm even using this in Devel::FIXME. Fun fun fun!

        -nuffin
        zz zZ Z Z #!perl
      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>. ...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://428418]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (3)
As of 2024-03-28 16:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found