in reply to BEGIN and compile-time

If the modules report version numbers (which normally is expected of modules), it might be best to use the following form of use;
use Module VERSION # e.g. use Foo::Bar 5.01; # <- just v5.01 please, TYVM # or better still eval "use Foo::Bar 5.01" or do { use Foo::Bar } # fallback incase the 'required' version isnt available
That way, your script works regardless of whether the module is moved about on the filesystem or your script is used on another machine.

Replies are listed 'Best First'.
Re^2: BEGIN and compile-time
by ikegami (Patriarch) on Nov 09, 2006 at 05:58 UTC

    That doesn't do what you expect. The second use Foo::Bar gets executed first, and it gets executed unconditionally.

    Why would you want to fallback to the same module anyway? That's the same as not putting a version in the first place! If you wanted to fallback to a different module, the following will do:

    BEGIN { my $module = 'Foo::Bar'; require Foo::Bar; eval { Foo::Bar->VERSION(5.01) }; if ($@) { warn(...); $module = 'Foo::Baz'; require Foo::Baz; } import $module qw( ... ); }

    VERSION is documented in UNIVERSAL.

      You're right, and i seem to have overseen the fact that use; statements take execution precedence. Moreover, this particular eval() doesn't work too well as a truth assertion (blaming use) and the subsequent use; gets executed no matter what.

      Why fallback to the same module?? Well, maybe we do need it but ideally we'd like to take control and load the one version of it identified by a supplied version number but if the module at that version isn't available, prepare the script for the standard version and continue, something along the lines of OP's wants.

      So, as has already been examined, "use lib '../'; use Foo::Bar;" seems to have worked simply and elegantly.. but "use lib" places entries at the beginning of @INC and what if non-standard versions of ../strict.pm or ../warnings.pm existed? The user might inadvertently load these modules (OUCH!!) rather than the ones in the original @INC. Placing entries at the beginning of @INC is a bad idea, the risk of polluting the package with bogus code as a result of namespace clashes is real.
      { use lib ".."; use strict; # "../strict.pm" loaded instead of the real one }
      Placing entries at the end of @INC means that modules if found in the original @INC take precendence and are loaded instead of the ones you want loading. The chances are of such a case are slim but entirely possible especially if the user isn't aware of the perl module namespace. Even otherwise, the user might be patching/updating the an already existant module and can ensure the testing release is loaded by using it's $VERSION (wherever the new .pm is located via @INC). Placing entries at the end of @INC is generally safer.

      As an added benefit, using the $Foo::Bar::VERSION to validate loading Foo::Bar also ensures that your code is guaranteed to run under Foo::Bar, alternate versions of the module might break something.

      So if the OP wanted to load a non-standard Foo::Bar (let's assume v5.00) instead of a standard Foo::Bar (v5.01) and provided the version numbers reported by the 2 modules were different (they really ought to be) ...
      #!/usr/bin/perl -W BEGIN { push @INC, ".." } use strict; # "../strict.pm" untouched eval "use Foo::Bar 5.01 qw| raz baz taz |"; # try loading v5.01 if ($@) { # if loading v5.01 failed warn "@_"; # fallback to using standard (v5.00) use Foo::Bar qw| baz |; # raz and taz arent valid tags here } ...

        Placing entries at the end of @INC means that modules if found in the original @INC take precendence and are loaded instead of the ones you want loading. The chances are of such a case are slim but entirely possible especially if the user isn't aware of the perl module namespace.

        I'm not sure why you are advocating adding to the back of @INC or how it relates to the thread, but adding to the front of @INC is definitely the way to go.

        There are two reasons to add to @INC:

        • To have require locate a module that wouldn't get located otherwise. In this case, it doesn't matter if you add to the front or back of @INC since there's only one copy of the module.

        • To have require locate the prefered version of a newer module when require would have locate another version of the module otherwise. In this case, you need to add to the front of @INC.

        So placing it at the front is claearly preferable. There are no reasons to place it at the back.

        what if non-standard versions of ../strict.pm or ../warnings.pm existed?

        You're inventing problems. If .. was added to the end of @INC, your non-standard strict.pm would never get loaded, so why did you write it?


        Now back on topic,

        eval "use Foo::Bar 5.01 qw| raz baz taz |"; # try loading v5.01 if ($@) { # if loading v5.01 failed warn "$@"; # fallback to using standard (v5.00) use Foo::Bar qw| baz |; # raz and taz arent valid tags here }

        You still haven't addressed the problem I mentioned (and fixed) in my previous post. The second use will get executed first, and unconditionally. Since both use load the same module, all you'll get is a Subroutine baz redefined warning. However, it's skirting with danger.

        Furthermore, it doesn't really make sense to import raz and taz from 5.01 if you don't create replacements when loading from <5.01.

        BEGIN { require Foo::Bar; eval { Foo::Bar->VERSION(5.01) }; if ($@) { warn("Warning: Foo::Bar not desired version 5.01. " . "Proceeding anyway\n"); import Foo::Bar qw( baz ); *raz = sub { ... }; *taz = sub { ... }; } else { import Foo::Bar qw( raz baz taz ); } }
Re^2: BEGIN and compile-time
by Firefly258 (Beadle) on Nov 10, 2006 at 14:28 UTC
    I managed to find out why my initial  eval " use ..." attempt failed. use; cannot be used in expressions . Hence, it does not return a value (so it's practically useless in truth evaluation constructs) even on a successful run like in the following..
    eval "use Foo::Bar" or do { warn "eval failed" }
    Here eval"" always evaluates to false as use; returns nothing and we end up with the do BLOCK always being executed.

    But getting eval"" to return a TRUE value on a successful eval changes that.
    eval "use Foo::Bar; 1" or do { warn "eval failed" }
      It is more appropriate to test $@.
      eval "use Foo::Bar"; if ($@) { warn "Eval failed: $@"; }

        I like

        eval { require Foo::Bar } or warn "Eval failed: $@";

        Since require only returns true or an exception. The import can be done later, although I usually don't want to import from a dynamically loaded module.

        Update: Oops, silly me! Replaced eval "" with eval {}. Thanks tye.

        It is more appropriate to test $@.

        Oh, I disagree. There are too many cases where $@ doesn't survive. It is more fool-proof (and one of my "best practices") to check the return value of eval "....; 1" to determine whether or not eval failed (and then to use $@ to report what the error was, though $@ might no long contain this information).

        - tye        

      Ikegami, you're right as always. Perhaps  eval "use Foo 5.01; 1" or do { require Foo; ... } does it.

      Thanks tye for clearning up that little something, it made me take a closer look at sub VERSION.

      Here's an attempt to get  use Module VERSION to load the right VERSION.
      $ cat a/strict.pm package strict; our $VERSION = 1.04; sub VERSION { my ($module, $version) = @_; return $VERSION unless $version; until ($VERSION == $version) { shift @INC; delete $INC{"strict.pm"}; eval " require strict; 1; "; $VERSION = $strict::VERSION; } return $VERSION; } $ cat ./strict #!/usr/bin/perl -Wl BEGIN { eval { local @INC = @INC; unshift @INC, qw|a b c|; eval " use strict 1.03 qw|subs vars refs|; 1; "; warn "$@" if $@; }; } print "strict using $INC{'strict.pm'} version $strict::VERSION "; $ ./strict strict using /usr/share/perl/5.8/strict.pm version 1.03
      It's kludgy and noisy but it works.
        • Putting the code in VERSION makes it less useful.
        • Gave function a prototype that will allow user to omit quotes around module name.
        • The module name was both a parameter and hardcoded. Removed the hardcoding.
        • Broke infinite loop if version not found.
        • Don't try to load twice from the same @INC path.
        • Made strict-safe and warning-safe.
        use Carp qw( croak ); sub require_exact_ver(*$) { my ($pkg, $desired_ver) = @_; { my $ver = $pkg->VERSION; return 1 if defined($ver) && $ver == $desired_ver; } my $file = $pkg; $file =~ s{::}{/}g; $file .= '.pm'; foreach $inc (@INC) { delete $INC{$file}; local @INC = $inc; eval { require $file } or next; my $ver = $pkg->VERSION; return 1 if defined($ver) && $ver == $desired_ver; } delete $INC{$file}; croak("Unable to load $pkg version $desired_ver"); }

        You might get lots of redefined warnings. Other hard to debug problems might also surface. To avoid those, you might want to take CPAN's approach to determining a module's version. CPAN searches for $VERSION in the source and executes only that line (more or less). Since the module isn't actually loaded, no redefined problems, etc.

        use Carp qw( croak ); use File::Spec qw( ); sub require_exact_ver(*$) { my ($pkg, $desired_ver) = @_; my $file = $pkg; $file =~ s{::}{/}g; $file .= '.pm'; if ($INC{$file}) { # Module already loaded. my $ver = $pkg->VERSION; return 1 if defined($ver) && $ver == $desired_ver; croak("$pkg version $ver already loaded (want $desired_ver)"); } local $/; foreach $inc (@INC) { next if ref($inc); # XXX my $path = File::Spec->catfile($inc, $file); open(my $fh, '<', $path) or next; my $file = <$fh> or next; my ($ver_cmd) = $file =~ /\$VERSION\s*=([^\n;]);/ or next; my $ver = eval $ver_cmd or next; $ver != $desired_ver or next; # woot! require $path; } croak("Unable to locate $pkg version $desired_ver"); }
        A much improved and refined sub VERSION() for the implicit approach.

        sub VERSION { my ($module, $version) = @_; return $VERSION unless $version; # satisfy UNIVERSAL::VERSION() do { require File::Spec; require File::Basename; my $file = File::Spec->catfile( split /::/, $module ) . ".pm"; my $dir; $dir = File::Basename::dirname( $dir || $INC{$file} ) for 0 .. $module =~ /::/g; for my $dir ( local @INC = grep { !/^$dir$/ && -e File::Spec-> +catfile( $_, $file ) } @INC ) { open MOD, "<", File::Spec->catfile( $dir, $file ) or warn +"$@"; grep { /(.*VERSION.*)/ and eval "eval '$1' == $version" and do { delete $INC{$file}; eval " require $module; $module->VERSION " == $ver +sion and do return $version ; shift @INC; } } <MOD>; } require Carp; Carp::croak(" Loading $module ($version) failed. "); } unless ($version == $VERSION); }
        A loader hook for @INC for explicit version control, basically just the above modified slightly.
        BEGIN { sub my_use { my ($arg_ref, $module) = @_; my $version; local @INC; (undef, $version, @INC) = @$arg_ref; require File::Spec; for my $dir ( grep { -e File::Spec->catfile( $_, $module ) } @ +INC ) { my $file = File::Spec->catfile( $dir, $module ); open MOD, "<", $file or warn "failed opening $file : $@"; grep { /(?:VERSION\s*=(.*))/ and eval "eval '$1' || 0" == $version and do { open my $fh, "<", $file or warn "open $file failed + : $@"; close MOD; return $fh; } } <MOD>; } } { local @INC = [ \&my_use, "1.03", qw|a b c|, @INC ]; eval "use strict"; } { local @INC = [ \&my_use, "5.01", @INC, qw|a b c|]; eval "use Foo::Bar"; } }
        There's bound to be room for correction and improvement, especially to creating a simple and elegant data structure to house information about module and version numbers.