Why perl tries to reload module when it is already loaded?

Viewed 79

I was trying to install Carton into local directory for my project and got error:

--> Working on Test::Deep
Fetching...
...
t/isa.t ..................... Can't locate Mojo/Base.pm in @INC (you may need to install the Mojo::Base module) (@INC contains: CODE(0x557913d006d0) t/lib /home/kes/.cpanm/work/1626520042.29670/Test-Deep-1.130/blib/lib /home/kes/.cpanm/work/1626520042.29670/Test-Deep-1.130/blib/arch /home/kes/work/projects/tucha/monkeyman/local/lib/perl5/x86_64-linux /home/kes/work/projects/tucha/monkeyman/local/lib/perl5 /home/kes/work/projects/tucha/monkeyman/lib /home/kes/perl5/perlbrew/perls/perl-5.35.1/lib/site_perl/5.35.1/x86_64-linux /home/kes/perl5/perlbrew/perls/perl-5.35.1/lib/site_perl/5.35.1 /home/kes/perl5/perlbrew/perls/perl-5.35.1/lib/5.35.1/x86_64-linux /home/kes/perl5/perlbrew/perls/perl-5.35.1/lib/5.35.1 CODE(0x557913d00670) .) at /home/kes/work/projects/tucha/monkeyman/lib/A.pm line 2.
BEGIN failed--compilation aborted at /home/kes/work/projects/tucha/monkeyman/lib/A.pm line 2.
Compilation failed in require at /home/kes/perl5/perlbrew/perls/perl-5.35.1/lib/5.35.1/base.pm line 138.
    ...propagated at /home/kes/perl5/perlbrew/perls/perl-5.35.1/lib/5.35.1/base.pm line 160.
BEGIN failed--compilation aborted at t/isa.t line 133.

This error occur because my application has A module, but t/isa.t defines already its own package A which is loaded into memory already.

# Test::Deep:t/isa.t:120
package A;

use Test::Deep;
@A::ISA = qw( Test::Deep );

{
  ::ok(A->isa("Test::Deep"), "U::isa says yes");
  ::ok(! A->isa("Test"), "U::isa says yes");
}


{
  package C;
  use base 'A';    # <<<< this cause error
}

But why use base 'A' tries to reload package A from disk while package A is already loaded into memory?

perl -v 5.35.1

1 Answers

You are experiencing the difference between packages and modules. A package is a namespace. A module is a file. It's easy to confuse them because by convention, the definition of the Foo::Bar::Baz namespace will be in file Foo/Bar/Baz.pm.

When you write:

use Foo::Bar::Baz;

Perl interprets that as two instructions:

  1. Load the file Foo/Bar/Baz.pm if it's not already loaded.
  2. Call the method import on the Foo::Bar::Baz namespace.

(And use base 'Foo::Bar::Baz' does similar, except instead of #2, it does some funky stuff with inheritance.)

So in your case, when you do use base 'A', Perl will do #1 unless file A.pm is already loaded. Yes, you've already defined some stuff in the A namespace, but that doesn't matter.

A few different solutions for you.

Trick Perl into thinking A.pm is already loaded

Adding this line somewhere before use base 'A' will trick Perl into thinking A.pm has already been loaded.

BEGIN { $INC{'A.pm'} = __FILE__ };

use parent

Do this instead of use base 'A':

use parent '-norequire', 'A';

parent is the more modern version of base and has a -norequire option to skip step #1.

use neither base nor parent

All those modules are doing for you are setting the @ISA variable for you. You can do that yourself.

{
  package C;
  our @ISA = 'A';
}
Related