Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Controlling "use" statements

by nodice (Initiate)
on Jun 13, 2007 at 17:01 UTC ( [id://621029]=perlquestion: print w/replies, xml ) Need Help??

nodice has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

Forgive me if this question is naive/obvious...

I'm currently working on a project where I keep separate "working" and "development" versions. Currently, the development version uses

use strict; use warnings; use Data::Dumper; use CGI::Carp; use etc...
I don't need this stuff for the working version, and therefore don't want it to load. Instead of commenting out these lines, is it possible to programmatically control which lines are used and which are not?

For example:

#!/user/bin/perl # BEGINs run FIFO, so set the global # in its own block. BEGIN { # A global switch that would # ideally control several options # throughout the program and would # be the only difference between # the "working" and "development" # versions. our $DEBUG = 1; } # Now set which things to import. BEGIN { # These will only be used in the # debugging version. if ($DEBUG) { use strict; use warnings; use Data::Dumper; use CGI::Carp; } # These are needed by the program # regardless. use CGI::Minimal; use HTML::Template; # ... others, etc. }
I know this doesn't work, but hopefully this is enough to give an idea of what I'm trying to accomplish.

Thanks in advance.

Replies are listed 'Best First'.
Re: Controlling "use" statements
by doom (Deacon) on Jun 13, 2007 at 17:28 UTC
    Yes, but you don't want to use "use" to do it, because it acts at compile time (it has an implicit BEGIN wrapped around it already). On the other hand requre acts at runtime, which means it can be used to conditionally load modules as needed. If you want it to behave like use, though, you also need to call import on the module, right after doing a require.

    There's a gotcha to require semantics though, it requires a "bareword", which is fine if you want to do something like this:

    if ($DEBUG) { require Data::Dumper; import Data::Dumper; }

    But if you want to put the module names in variables, then you'll probably need something like this:

    my @debug_modules = qw( Data::Dumper CGI Test::Deep ); foreach my $mod (@debug_modules) { eval "require $mod"; import $mod; }
    (Note, some folks are down on eval STRING, but this is a case where it's necessary.)

    If you want to read up on this, look into things like "perlmod", and the "use" and "require" sections of "perlfunc". Do not however, waste your time looking for "import" in perlfunc. It's not a "function", it's more like a reserved name for a subroutine.

      Thank you, this is very useful. As a followup, what would be the correct way to include certain features with the module?

      For example, I want the equivalent of

      use CGI::Carp qw(fatalsToBrowser set_message)

      I know that

      require CGI::Carp qw(...)

      won't work, because it can only handle barewords. What is the correct way to do this?

      Thanks.
        use CGI::Carp qw(fatalsToBrowser set_message)

        is a shortcut for

        BEGIN { require CGI::Carp; import CGI::Carp qw(fatalsToBrowser set_message); }

        You can easily add an if in there.

        BEGIN { if ($DEBUG) { require CGI::Carp; import CGI::Carp qw(fatalsToBrowser set_message); } }
        BEGIN { use vars qw($devel); $devel = 0; # production } BEGIN { my @args = $devel ? qw(fatalsToBrowser set_message) : (); require CGI::Carp; CGI::Carp->import(@args); }

        The equivalent of use Module LIST is BEGIN { require Module; Module->import(LIST) }.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Do not however, waste your time looking for "import" in perlfunc. It's not a "function", it's more like a reserved name for a subroutine.
      But there is a perlfunc entry for it, just in case you do look.
Re: Controlling "use" statements
by almut (Canon) on Jun 13, 2007 at 17:27 UTC

    You could use the module if, e.g.

    BEGIN { our $DEBUG = 1 } use if $DEBUG, Data::Dumper; # ...

    or

    BEGIN { sub DEBUG {1} } use if DEBUG, Data::Dumper; # ...

      There's one problem with if: There's no equivalent to use Module ();.

      The conditional version of use Module; is use if $cond, 'Module';
      The conditional version of use Module LIST; is use if $cond, Module => LIST;

      However, use if $cond, Module => (); is the same as use if $cond, 'Module';

      Furtunlately, this doesn't seem to be a concern for the OP.

        That's right.  If you desperately needed that functionality, you could patch if.pm as shown below. (In case you'd rather not mess with the system version, just create a copy in some local lib directory and make that directory be searched before the system ones.)

        You could then for example write

        use if $DEBUG, 'Data::Dumper' => undef;

        to import nothing, just like with

        use Data::Dumper ();

        As the module is actually rather short, here is the full code:

        package if; sub work { my $method = shift() ? 'import' : 'unimport'; die "Too few arguments to `use if' (some code returning an empty lis +t in list context?)" unless @_ >= 2; return unless shift; # CONDITION my $p = $_[0]; # PACKAGE (my $file = "$p.pm") =~ s!::!/!g; require $file; # Works even if $_[0] is a keyword (like open) return if @_==2 && !defined $_[1]; # <--- ADDED my $m = $p->can($method); goto &$m if $m; } sub import { shift; unshift @_, 1; goto &work } sub unimport { shift; unshift @_, 0; goto &work } 1;

        When being passed undef, the line marked with "<--- ADDED" would make the sub return before executing the real import() of the target module via goto &$m (where $m is referring to import in this case).

        (Not well tested! &mdash use at your own risk...)

Re: Controlling "use" statements
by ikegami (Patriarch) on Jun 13, 2007 at 17:57 UTC
    People are suggesting solutions. Let me identify the problem:
    BEGIN { # These will only be used in the # debugging version. if ($DEBUG) { use strict; use warnings; use Data::Dumper; use CGI::Carp; } # These are needed by the program # regardless. use CGI::Minimal; use HTML::Template; # ... others, etc. }

    Like BEGIN blocks, use statements execute as soon as they are compiled. Your use CGI::Carp; executes as soon as it is compiled, with no regard to whether that code is reached at run-time. That means the if ($DEBUG) has no bearing on whether use statements inside the if are executed or not.

    Secondly, strict and warnings are pragmas. They only affect the lexical block in which they are located (and nested blocks). use strict; would only work for the body of the if.

      Secondly, strict and warnings are pragmas. They only affect the lexical block in which they are located (and nested blocks). use strict; would only work for the body of the if.

      Does this mean they are "uncontrollable" by definition?

        No, the pragmas are controllable with use and no, e.g.
        use strict; $MyClass::foo = 'boink'; sub bar { no strict; # strict turned off for this sub } ... # strict again in effect

        They live in files which are imported, like modules - but they act like switches and turn on and off certain flags at compile time, for their lexical scope.

        There's no reason to turn strict or warnings off for a "working" version, once the "development" versions behave with them. Again: *no* *reason*. And in case you ask again - no, there is no reason, period.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

        No.

        The following works:

        my $DEBUG; BEGIN { $DEBUG = $ARGV[0]; } use if $DEBUG, 'strict'; # <-- The compiler is here because # Perl executes use statements # as soon as it compiles them. # "strict" will work until the # end of this block (namely # until the end of the file). print($test); # strict error if $DEBUG

        The following works:

        my $DEBUG; BEGIN { $DEBUG = $ARGV[0]; if ($DEBUG) { require strict; import strict; } } # <-- The compiler is here because # Perl executes BEGIN blocks # as soon as it compiles them. # "strict" will work until the # end of this block (namely # until the end of the file). print($test); # strict error if $DEBUG

        The point, however, is that the following doesn't work:

        my $DEBUG; BEGIN { $DEBUG = $ARGV[0]; if ($DEBUG) { eval <<'__EOI__'; use strict; # <-- The compiler instance used # by eval is here because # Perl executes use statements # as soon as it compiles them. # "strict" will work until the # end of this block (namely # until the end of the code # passed to eval). __EOI__ } } print($test); # No strict error

        Actually, I thought the second one wouldn't work either, but it does for the reason I gave in the comments.

Re: Controlling "use" statements
by adrianh (Chancellor) on Jun 14, 2007 at 12:47 UTC
    use strict; use warnings;

    Please reconsider disabling these in your live version. The difference in load/run time is trivial and I can pretty much guarantee that you'll wish you had warnings on at some point on the runtime box :-)

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-03-29 11:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found