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

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

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:
Load the file Foo/Bar/Baz.pm if it's not already loaded.
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

Does package compiled partially? How to prevent this?

I have code (some lines are removed):
package MaitreD::Command::bank_statement;
use Mojo::Base 'Mojolicious::Command';
sub run {
...
my $payments = read_file( $file ); # line 58
...
}
use XBase; # line 174
sub read_file {
...
}
1;
I run my application. And then do two http requests to this app. Controller runs this command as:
$c->app->commands->run( bank_statement => $upload );
I get next error (this one is expected):
Can't locate XBase.pm in #INC (you may need to install the XBase module) (#INC contains: /opt/monkeyman/lib /opt/monkeyman/local/lib/perl5/x86_64-linux /opt/monkeyman/local/lib/perl5 /opt/monkeyman/lib /opt/monkeyman/local/lib/perl5/5.24.1/x86_64-linux /opt/monkeyman/local/lib/perl5/5.24.1 /opt/monkeyman/local/lib/perl5/x86_64-linux /opt/monkeyman/local/lib/perl5 /opt/perlbrew/perls/perl-5.24.1/lib/site_perl/5.24.1/x86_64-linux /opt/perlbrew/perls/perl-5.24.1/lib/site_perl/5.24.1 /opt/perlbrew/perls/perl-5.24.1/lib/5.24.1/x86_64-linux /opt/perlbrew/perls/perl-5.24.1/lib/5.24.1 .) at /opt/monkeyman/lib/MaitreD/Command/bank_statement.pm line 174.
BEGIN failed--compilation aborted at /opt/monkeyman/lib/MaitreD/Command/bank_statement.pm line 174.
Compilation failed in require at (eval 2620) line 1.
But when I did second request I got different error:
Undefined subroutine &MaitreD::Command::bank_statement::read_file called at /opt/monkeyman/lib/MaitreD/Command/bank_statement.pm line 58.
How MaitreD::Command::bank_statement::run could be run from controller if module MaitreD::Command::bank_statement compilation failed?
If understand correct the module MaitreD::Command::bank_statement was compiled partially to 174 line. So next http request to app can call MaitreD::Command::bank_statement::run and when 58 line is reached I get Undefined subroutine &M::C::b::read_file called because nothing is compiled after 174 line.
How to prevent partial compilation?
I want if there are some errors occur then nothing from MaitreD::Command::bank_statement should be available
It seems you should be focusing on ensuring use XBase actually succeeds, beause presumably it's there for a reason and it's needed for the rest of the program to work.
Why does it fail? Fix that first and the partial compilation isn't a problem.
In this case, why can't perl find the module?
Is it possible the Command::bank_statement class isn't used directly, but only when it's being run, so maybe the current working directory changed between the program start and the time $c->app->commands->run( bank_statement => $upload ); was called?
If that's the case, try loading the command class earlier. e.g. add this to the Mojo application class (probably something like lib/MaitreD.pm:
use MaitreD::Command::bank_statement;

why does perl's Module::Load::Conditional::check_install find uninstalled module?

--> perl -v
This is perl 5, version 24, subversion 0 (v5.24.0) built for x86_64-linux
I'm trying to conditionally load a perl module if it's installed using...
#!/usr/bin/env perl
use strict;
use Module::Load::Conditional qw[can_load check_install requires];
if(check_install( module => 'dvm_common')) {
use dvm_common;
print "Looks like dvm_common is installed, so it was loaded.\n";
} else {
print "Looks like dvm_common is not installed.\n";
}
exit;
Won't compile, complains about missing module...
--> perl -c ./mod_load_cond_test.pl
Can't locate dvm_common.pm in #INC (you may need to install the dvm_common module) (#INC contains: etc... .) at ./mod_load_cond_test.pl line 6.
BEGIN failed--compilation aborted at ./mod_load_cond_test.pl line 6.
I thought the whole point of using this was to test for the existence of the module before loading it ?
use is a compile-time statement, so the interpreter will load a module immediately when it it passing through the source and encounters a use statement, as if you had written
BEGIN { require Module; Module->import }
To load a module at run-time, you can use require
if(check_install( module => 'dvm_common')) {
require dvm_common;
dvm_common->can("import") && dvm_common->import; # optional
...
or string eval:
if(check_install( module => 'dvm_common')) {
eval "use dvm_common;1" or die $#;
...

How to calculate time average from a array list

I have a time array list in format h:mm:ss. I want to calculate the average time of this array.
I tried below code but there is some problem with the packages. I successfully installed Duration package but now complier is throwing error on Duration::Parse package.
PS: I am using Dwinperl as the editor on windows.
use Time::Duration::Parse qw(parse_duration);
use Time::Duration qw(duration);
use List::Util qw(sum);
my $count = #time;
my $sum = sum map {parse_duration($_) } #time;
my $avg = $sum / $count;
print duration($sum, 3), "--Total Time\n";
print duration($avg, 3), "--Avg Time\n";
this is the error message that I am getting.
Can't locate Time/Duration/Parse.pm in #INC (#INC contains: C:/Dwimperl/perl/sit
e/lib C:/Dwimperl/perl/vendor/lib C:/Dwimperl/perl/lib .) at time.pl line 7.
BEGIN failed--compilation aborted at time.pl line 7.
It seems perl could not find package Time::Duration::Parse installed within list of #INC paths
Type following command in terminal to check if perl can find your module by default
perldoc -l Time::Duration::Parse
If above command didn't give you installed location of desired module,
Make sure you have installed required modules
try adding following line to your perl code to add custom installed path of module
use lib '/path/to/module';

Dancer DBIC Could not load schema_class

I have a Dancer app that is working. I am using DBIC for the database.
I want to add some tables to the database, and so I created new files for those. However, now when I restart the app, I get the following errors
Error while loading ././bin/app.pl: Could not load schema_class MyApp::Schema at /usr/local/share/perl/5.14.2/Dancer/Plugin/DBIC.pm line 42.
Compilation failed in require at ././bin/app.pl line 5.
BEGIN failed--compilation aborted at ././bin/app.pl line 5.
When I remove the new files, everything works fine.
Here is my Schema.pm file
package MyApp::Schema;
# Created by DBIx::Class::Schema::Loader
# DO NOT MODIFY THE FIRST PART OF THIS FILE
use strict;
use warnings;
use base 'DBIx::Class::Schema';
__PACKAGE__->load_namespaces;
1;
Where can I get more information about the error and what I did wrong?
Since you are using __PACKAGE__->load_namespaces, all new classes will loaded, which explains why the simple existence of the new class files cause an error. If there is a syntax error in your new class files, then loading the schema will die.
You can use perl -c on the new class files to ensure they will compile and get a better idea of the problem if they do not.

Why does my Perl unit test fail in EPIC but work in the debugger?

Has anyone ever experienced a unit test that fails and when they tried to debug it to find out where the failure was occurring, the unit test succeeds when running the code in the debugger?
I'm using Eclipse 3.5.1 with EPIC 0.6.35 and ActiveState ActivePerl 5.10.0. I wrote module A and module B both with multiple routines. A routine in module B calls a bunch of routines from module A. I'm adding mock objects to my module B unit test file to try to get more complete code coverage on module B where the code in module B tests to see if all the calls to module As routines fail or succeed. So I added some mock objects to my unit test to force some of the module A routines to return failures, but I was not getting the failures as expected. When I debugged my unit test file, the calls to the module A routine did fail as expected (and my unit test succeeds). When I run the unit test file as normal without debugging, the call to the mocked Module A routine does not fail as expected (and my unit test fails).
What could be going on here? I'll try to post a working example of my problem if I can get it to fail using a small set of simple code.
ADDENDUM: I got my code whittled down to a bare minimum set that demonstrates my problem. Details and a working example of the problem follows:
My Eclipse project contains a "lib" directory with two modules ... MainModule.pm and UtilityModule.pm. My Eclipse project also contains at the top level a unit test file named MainModuleTest.t and a text file called input_file.txt which just contains some garbage text.
EclipseProject/
MainModuleTest.t
input_file.txt
lib/
MainModule.pm
UtilityModule.pm
Contents of the MainModuleTest.t file:
use Test::More qw(no_plan);
use Test::MockModule;
use MainModule qw( mainModuleRoutine );
$testName = "force the Utility Module call to fail";
# set up mock utility routine that fails
my $mocked = new Test::MockModule('UtilityModule');
$mocked->mock( 'slurpFile', undef );
# call the routine under test
my $return_value = mainModuleRoutine( 'input_file.txt' );
if ( defined($return_value) ) {
# failure; actually expected undefined return value
fail($testName);
}
else {
# this is what we expect to occur
pass($testName);
}
Contents of the MainModule.pm file:
package MainModule;
use strict;
use warnings;
use Exporter;
use base qw(Exporter);
use UtilityModule qw( slurpFile );
our #EXPORT_OK = qw( mainModuleRoutine );
sub mainModuleRoutine {
my ( $file_name ) = #_;
my $file_contents = slurpFile($file_name);
if( !defined($file_contents) ) {
# failure
print STDERR "slurpFile() encountered a problem!\n";
return;
}
print "slurpFile() was successful!\n";
return $file_contents;
}
1;
Contents of the UtilityModule.pm file:
package UtilityModule;
use strict;
use warnings;
use Exporter;
use base qw(Exporter);
our #EXPORT_OK = qw( slurpFile );
sub slurpFile {
my ( $file_name ) = #_;
my $filehandle;
my $file_contents = "";
if ( open( $filehandle, '<', $file_name ) ) {
local $/=undef;
$file_contents = <$filehandle>;
local $/='\n';
close( $filehandle );
}
else {
print STDERR "Unable to open $file_name for read: $!";
return;
}
return $file_contents;
}
1;
When I right-click on MainModuleTest.t in Eclipse and select Run As | Perl Local, it gives me the following output:
slurpFile() was successful!
not ok 1 - force the Utility Module call to fail
1..1
# Failed test 'force the Utility Module call to fail'
# at D:/Documents and Settings/[SNIP]/MainModuleTest.t line 13.
# Looks like you failed 1 test of 1.
When I right click on the same unit test file and select Debug As | Perl Local, it gives me the following output:
slurpFile() encountered a problem!
ok 1 - force the Utility Module call to fail
1..1
So, this is obviously a problem. Run As and Debug As should give the same results, right?!?!?
Both Exporter and Test::MockModule work by manipulating the symbol table. Things that do that don't always play nicely together. In this case, Test::MockModule is installing the mocked version of slurpFile into UtilityModule after Exporter has already exported it to MainModule. The alias that MainModule is using still points to the original version.
To fix it, change MainModule to use the fully qualified subroutine name:
my $file_contents = UtilityModule::slurpFile($file_name);
The reason this works in the debugger is that the debugger also uses symbol table manipulation to install hooks. Those hooks must be getting installed in the right way and at the right time to avoid the mismatch that occurs normally.
It's arguable that it's a bug (in the debugger) any time the code behaves differently there than it does when run outside the debugger, but when you have three modules all mucking with the symbol table it's not surprising that things might behave oddly.
Does your mocking manipulate the symbol table? I've seen a bug in the debugger that interferes with symbol table munging. Although in my case the problem was reversed; the code broke under the debugger but worked when run normally.