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

Take for instance, this simple package:

package TestPackage; BEGIN { print STDERR "in BEGIN\n" } INIT { print STDERR "in INIT\n" } 1;
Then load it with this code (compile-time):
use TestPackage;
And you get this output:
in BEGIN in INIT
Now, load it with this code (run-time):
require TestPackage;
and you get this output.
in BEGIN Too late to run INIT block at TestPackage.pm line ...
Now I understand why this is so. The INIT blocks run in FIFO order right before the perl runtime begins. So obviously one cannot run an INIT block if one is already within the executing runtime.

My question to the monastary is; Is there any way around this? Is there anyway when my module is required, that I can run my INIT blocks without forcing the user to manually do something (call some other initialization routine)? Is it at all possible to determine if the runtime is underway yet, therefore detemining if we were loaded with "use" or "require"? I have looked at the $^C flag but unless perl was started with the -c switch it does me no good. Has anyone had these issues before, and if so what were your solutions?

Also, does anyone know more about the UNITCHECK block which may be in perl 5.9.1 or 5.10? I am under the impression this might solve my problem, but I only know a little about it, any more info would be appreciated.

-stvn

Replies are listed 'Best First'.
Re: INIT blocks and runtime code loading
by broquaint (Abbot) on Apr 26, 2004 at 23:55 UTC
    Assuming a relatively recent version of perl (5.7.1+ IIRC) you can execute INIT at your discretion. However the trick is in avoiding executing them twice. Here's a very bare bones example of executing INIT blocks from both use and require
    package testpkg; BEGIN { warn "in BEGIN\n" } { no warnings; INIT { return if $INIT_DONE++; warn "in INIT\n"; } } { use B; $_->object_2svref->() for grep { $_->GV->STASH->NAME eq __PACKAGE__ } B::init_av->ARRAY; } 1;
    And some usage code
    $ perl -we 'use testpkg;' in BEGIN in INIT $ perl -we 'require testpkg;' in BEGIN in INIT
    Unfortunately it uses a rather horrific bodge of a package variable and post-increment behaviour, but nonetheless, it works :) Hopefully there's some very straight-forward solution for determining what state perl is in when a piece of code is executing, but currently I'm out of ideas.
    HTH

    _________
    broquaint

      One wrinkle on broquaint's kludge is its interaction with an import subroutine.
      'use'ing a module normally goes through:
      BEGIN
      import
      INIT
      
      However, adding an import subroutine to the above code produces:
      BEGIN
      INIT
      import
      
      One possible workaround for this would be to move any INIT code to the end of the import subroutine. Yet even this is not a 100% solution as:
      use testpkg ();
      causes the import subroutine - and hence the moved INIT code - not be be executed.

      For the module I'm writing, I have a BEGIN block, an import subroutine, and an INIT block. The INIT block consists of just one function call. Therefore, I warn the user that if they want delayed evaluation of my module, they must do so as follow:

      eval { require MyModule; import MyModule qw/.../; MyModule::MyInit(); }

      Remember: There's always one more bug.