in reply to Re: Failed require looks like it succeeded
in thread Failed require looks like it succeeded

.. demands that a library file be included if it hasn't already been included. The file is included via the do-FILE mechanism, which is essentially just a variety of "eval"

I can't find a definition of exactly what "included" means, but you seem to be saying that if the file is found, and is readable, and an attempt to compile and load it is made, then the file is considered to have been "included," even if the compilation or loading fails. But in fact this is not the case. Here's a pair of examples that shows the inconsistency:

#foo.pl for (1..2) { eval "use bar"; print "attempt $_: ", ($@ ? 'fail' : 'succeed'), "\n"; } #bar.pm 0 #stdout attempt 1: fail attempt 2: fail # now change bar.pm to the following: die #stdout attempt 1: fail attempt 2: succeed

In the first case, perl attempts to load bar.pm twice, but in the second case it only tries once. So apparently if a module loads successfully but returns 0, it has not been "included," but if it fails to load then it has been "included?"

Incidentally, while preparing this example I discovered a falsehood in the documentation for "use." It says that use bar is exactly equivalent to BEGIN { require bar; import bar; } but if you substitute the latter for the former in the example, the results are different.

FWIW, here's my situation. My application has a large number of modules, only a few of which are used by any given user. Currently they're all require'd at startup, with the require inside an eval so that if a module fails to load, the user is notified that it is unavailable, but he can continue to use the other modules. To reduce startup time, I want to postpone loading each module until the user explicitly activates it by clicking on it in the gui. If the user clicks on a module and it fails to load, he gets an error dialog. But if he's obstinate enough to click on it a second time, currently the application acts as if the module loaded successfully, even though it really didn't. I'm going to have to keep track explicitly of which modules the application has already attempted to load, and avoid trying any module twice. First of all, this is kind of annoying because I thought this was exactly the kind of thing that "require" was supposed to take care of. Secondly, it means that if the user fixes whatever condition caused the module not to load the first time (maybe he installed a dependency, fixed a configuration file, whatever... these modules are third-party plugins, out of my application's control) he's going to have to restart the application, which ought to be unnecessary.

Thanks for your help.

Replies are listed 'Best First'.
Re^3: Failed require looks like it succeeded
by Anonymous Monk on Mar 21, 2005 at 09:01 UTC
    I can't find a definition of exactly what "included" means, but you seem to be saying that if the file is found, and is readable, and an attempt to compile and load it is made, then the file is considered to have been "included," even if the compilation or loading fails. But in fact this is not the case.
    No, he doesn't say that. You need to read perlfunc
Re^3: Failed require looks like it succeeded
by eric256 (Parson) on Mar 21, 2005 at 18:15 UTC

    You could always delete the entry in INC so that the second require works as expected. Another solution would be to have modules register themselves and only try to load unregistered module. I've used both solutions in systems where modules might be created/fixed while the main app is running but I don't want to restart the main app.


    ___________
    Eric Hodges
      Both of those approaches could be used to work around the problem in the examples I gave so far, but actually the situation is slightly more complicated:

      # foo.pl for (1..2) { eval "use bar"; print "attempt $_: ", ($@ || 'success'), "\n"; } # bar.pm eval "require baz"; if ($@) { die "bar will be unavailable because baz didn't load properly."; } 1; # baz.pm use missingdependency;

      So adding delete $INC{bar.pm} if $@; to foo.pl isn't sufficient--I also need to delete baz.pm. This would be easy if I could modify bar.pm, but I don't want to--bar.pm is a third-party plugin that was written for earlier versions of foo.pl, and I'd prefer not to break backwards compatibility for the plugins.

      But the following works, without any changes to bar.pm or baz.pm:

      # foo.pl for (1..2) { my %saveinc = %INC; eval "use bar"; if ($@) { for my $package(keys %INC) { delete $INC{$package} if ! exists $saveinc{$package}; } } print "attempt $_: ", ($@ || 'success'), "\n"; }

      I think that'll basically do the trick. Maybe I'll also delete the symbol table of the packages I delete from %INC, in case some of them actually loaded successfully, to avoid getting warnings about redefinitions later on.

      Thanks everyone for all the ideas.

Re^3: Failed require looks like it succeeded
by rir (Vicar) on Mar 21, 2005 at 20:20 UTC
    This may fix your immediate problem:
    # bar.pm BEGIN { eval { die } }
    or
    # bar.pm CHECK { die }
    Be well,
    rir