Perl `use` - import is not called twice? - perl

Doubt in perl basics use
it is somewhat similar to my other question Perl: Two packages in same file...
consider a perl script:
Script.pl
use INCLUDES;
INCLUDES.pm
package INCLUDES;
use Exporter;
############# MY DOUBT STARTS HERE ###############
use Module1;
use Module2;
##################################################
our #ISA = qw(Exporter);
our #EXPORT = qw();
sub import {
print 'INCLUDES imported to ' . caller . "\n";
}
Module1.pm
package Module1;
use strict;
use Exporter;
use INCLUDES; #####=> INCLUDES.pm 'use'd
our #ISA = qw(Exporter);
our #EXPORT = qw();
1;
Module2.pm
package Module2;
use strict;
use Exporter;
use INCLUDES; #####=> INCLUDES.pm 'use'd
our #ISA = qw(Exporter);
our #EXPORT = ();
1;
OUTPUT
D:\Do_analysis>Script.pl
INCLUDES imported to main
According to perl docs, use INCLUDES; in Module1 & Module2 => BEGIN {require 'INCLUDES.pm'; 'INCLUDES'->import();} . So, the import() should be called in Module1.pm , Module2.pm also.
I would expect the output as something like below,
EXPECTED OUTPUT ??
D:\Do_analysis>Script.pl
INCLUDES imported to main
INCLUDES imported to Module1
INCLUDES imported to Module2
But why is the execution not as expected?
UPDATED
This is what I am trying to achieve, by having the INCLUDES.pm file.
Note that: PACKAGE2 might want to access PACKAGE3, PACKAGE4 etc. Instead of useing all the modules inside PACKAGE2 seperately, I would like to create a Library INCLUDES and use it in all other modules.
Is this approach valid? or recommendable?
I appreciate any idea about how to achieve this.
Thanks!

If you were to move
use Module1;
use Module2;
below sub import { ... }, you'd get the expected behaviour.
The problem is that you execute Module1.pm before the compiler even reaches sub import in INCLUDES.pm.
When Module1.pm does use INCLUDES;, Perl says "oh, it's already loaded" (in response to require) and "oh, it doesn't have an import" (in response to import), since the part of INCLUDES.pm that would have created import hasn't been compiled yet. Same for Module2.pm.
Generally speaking, if you have a modules that include each other (directly or otherwise), you're doing something wrong (in terms of design), and you'll end up having to deal with such issues. You might find Mini-Tutorial: Mutual Use of Exporting Modules useful in dealing with these issues.

You have circular dependencies between your modules. main uses INCLUDES, INCLUDES uses Module1, and Module1 uses INCLUDES (Module2 is basically irrelevant here, except to act the same as Module1). If perl did the naïve thing, this would cause an infinite loop (or at least a stack overflow), as every time perl tried to compile INCLUDES it would lead to a compilation of Module1, and every time perl tried to compile Module1 it would lead to a compilation of INCLUDES.
Since we don't want that to happen, perl has to do something different. What it does instead is to ignore any request to compile a module that's already compiled or that it's already in the process of compiling. So the flow of events is:
Perl begins compiling script.pl and enters the package main.
Perl sees use INCLUDES;, loads INCLUDES.pm, and begins compiling it.
At the line package INCLUDES;, perl enters the package INCLUDES.
Perl sees use Module1;, loads Module1.pm, and begins compiling it.
At the line package Module1, perl enters the package Module1.
Perl sees the use INCLUDES;, realizes that it's already in the process of compiling INCLUDES.pm, and doesn't attempt to compile it again.
As a result of use INCLUDES; perl tries to call INCLUDES->import, but the import method hasn't been defined yet (we suspended compilation of INCLUDES at step 4 to start compiling Module1).
Compilation of Module1 completes and we go back to INCLUDES.
Perl calls Module1->import from INCLUDES.
Compilation of INCLUDES completes, including the use Module2 and the definition of sub import. Perl goes back to main.
INCLUDES->import is called from main and your message is printed.
Compilation of script.pl completes.
script.pl has nothing to do at runtime, and perl exits.

Related

'use lib' can't find Perl module

I'm on Windows 7, I downloaded a CPAN module called XML::XPath, and I want to use it in a script I wrote.
I cannot modify #INC on my machine, I cannot modify environmental variables on my machine, I cannot run make on my machine, and I cannot use a package manager on my machine.
So in order to use the module, I went to the CPAN website, downloaded the module's .tar.gz file, and unzipped it into a lib folder in my project. I did that because this guide suggested that I can use use lib in the context of my script to reference the downloaded module:
Adding a use lib statement to the script will add the directory to #INC for that specific script. Regardless who and in what environment runs it.
My script is called test.pl. I am trying to use the XML::XPath module to parse an XML file called test.xml. Here is an example of my directory structure:
C:/
→ sandbox/
test.pl
test.xml
→ lib/
→ XML-XPath-1.13/
XPath.pm
→ XPath/
XMLParser.pm
(etc.)
This is my test.pl script:
#!/usr/bin/perl
use strict;
use warnings;
use lib qw("C:/sandbox/lib/XML-XPath-1.13");
my $xml_file = "C:/sandbox/test.xml";
my $result;
if (-f $xml_file) {
$result = XML::XPath->new(filename => "$xml_file");
}
When I run this script with perl test.pl, the script fails with the following error:
Can't locate object method "new" via package "XML::XPath" (perhaps you forgot to load "XML::XPath"?) at test.pl line 10.
Can I resolve this error with use lib?
EDIT:
When I add use XML::XPath; to my test.pl script, as such:
#!/usr/bin/perl
use strict;
use warnings;
use lib qw("C:/sandbox/lib/XML-XPath-1.13");
I get an error like:
Can't locate XML/XPath.pm in #INC
But as stated above, I cannot physically modify #INC or recompile Perl on my machine.
This is not going to work. XML::XPath depends on the XML::Parser module which needs to be compiled. Since you do not have a complete toolchain with a C compiler available, there is no way you are getting this to run. You will have to search for a different way to handle your XML.
Just extracting a tarball only works when you have a pure-Perl module.
The easiest way to get a full Perl toolchain on Windows is to install Linux install the Strawberry Perl distribution. There's even a ZIP edition and a portable edition that can be installed on a thumb drive, no admin privileges required.
Your more immediate problem is that XML::XPath in version 1.13 seems to be a shoddy module with an unusual directory layout. The package you are trying to load is at XML-XPath-1.13/XPath.pm but should be at XML-XPath-1.13/lib/XML/XPath.pm. Why?
When you say use XML::XPath; this does two things. First, Perl searches for a file XML/XPath.pm in all #INC directories. Assuming you have something like
use FindBin;
use lib "$FindBin::Bin/lib/XML-XPath-1.13";
then perl would expect a file PROJECT/lib/XML-XPath-1.13/XML/XPath.pm which doesn't exist (where I'm using PROJECT as placeholder for the directory of your script).
Since the file PROJECT/lib/XML-XPath-1.13/XPath.pm does exist you could say use XPath;.
But then the second thing happens: The package is imported so that it can load subroutines or constants into your namespace. The use'd package name is now used as a class: XPath->import. However, the XPath.pm file does not contain the XPath package but the XML::XPath package, so there is no XPath class with an import method.
The import method call can be suppressed by providing an empty list in the use statement: use XPath ();.
Better than such hacks, you could use a more recent version of XML::XPath (version 1.13 is from 2003, but the newer version 1.42 is from 2007). Once you extract it you could use lib "$FindBin::Bin/lib/XML-XPath-1.42/lib" and then use XML::XPath;.
But this still won't work, because XML::XPath does use XML::Parser and you don't have that module installed. Now we are back to your main problem: XML::Parser needs to be compiled first because it contains a C extension, and also relies on an external library. Without a full toolchain you can't compile it.
First of all, you should properly install the module into lib rather than unzipping the distribution into lib.
Then, you'll need
use FindBin qw( $RealBin );
use lib "$RealBin/lib";
use XML::XPath qw( );

How to reuse mock objects in Perl unit tests?

I have a bunch of unit tests for my module, and I find that I copy the same mock and setup code from one to the other. How can I DRY that up and reuse the mock code?
I've placed this simple mock object next to my tests:
package MockObject;
1;
If I just say use MockObject; in the test case, make test cannot find MockObject. Makes sense; after all, it's not installed system wide and it's not next to the module under test.
I can run my tests with prove -I lib -I t t/*.t but I'd like to keep make test, if only for the laziness of typing a few characters less.
Since the mock object isn't a full module and shouldn't be officially installed anyway, I cannot and don't want to set TEST_REQUIRES in Makefile.PL.
Adding test => { FILES => 't/*.t', INC => 't/' } to Makefile.PL didn't help.
How can I (simply) reuse Perl mock code with the MakeMaker generated Makefile?
Setup PERL5LIB or add use lib in your script. I found FindBin package useful for setting up the lib path for tests.
use FindBin qw($Bin);
use lib "$Bin/../lib";
Where:
$Bin - path to bin directory from where script was invoked

Perl Par::Packer Can't find module issue

I have a perl program that uses WWW::Mechanize::Firefox on windows 7 32bit with strawberry perl.
It works fine with the command C:\>perl testcase.pl. When I compile it with C:\>pp -o testcase.exe testcase.pl it compiles with no errors.
When I run the testcase.exe it gives me the error:
Failed to connect to , Can't locate object method "setup" via package "MozRepl::Client" at MozRepl.pm line 224
The code I am using for testcase.pl is:
#!perl
use MozRepl;
use WWW::Mechanize::Firefox;
use warnings;
system('start firefox');
sleep(5);
$mech = WWW::Mechanize::Firefox->new;
Also note that a program without WWW::Mechanize::Firefox and MozRepl does work fine.
The problem has obviously been narrowed down to PAR::Packer not liking MozRepl, any idea what it might be?
PAR::Packer sometimes has a hard time identifying which modules need to be included in a PAR package in order to fulfill all of the requirements of the program you are trying to package.
It copes OK if the dependancies are loaded via plain 'use', or 'require' statements where the module to be loaded is a literal string, but it won't have much chance if the module is being loaded dynamically with something like:
require $myModuleToLoad;
Browsing the source code of MozRepl and related modules shows that they make heavy use of plugins loaded dynamically. I suspect that some of these are not being packaged.
You can manually specify module(s) to be included in the PAR package by adding -M Module::Name to the pp command line for each of the modules to be added (replacing Module::Name with the actual module name of course).
The hard part might be identifying which modules to include. One way to do this is to temporarily add something like this to the end of your script:
END { print "$_ -> $INC{$_}\n" foreach sort keys %INC; }
then run your script normally, not through PAR. It should list all the modules that were loaded. You can compare that with the actual modules present in the PAR package and add the missing ones using the -M option to pp.
You can see the modules inside your PAR file by opening it with an unzipping tool, such as 7zip. Or in Linux:
unzip -l {parfile}

Perl module dependency organization and include order

I am aware of cyclic module dependency in perl and the fact that it is very bad idea e.g.:
package ModuleA;
use ModuleB;
package ModuleB;
use ModuleA;
I want to ask if following model is safe and if it follows some best practicing rules:
package main;
use ModuleA;
use ModuleB;
package ModuleA;
use ModuleB;
use ModuleC;
package ModuleB;
use ModuleC;
Also I would like to ask if the order of use-ing modules have any impact? e.g. if
package main;
use ModuleA;
use ModuleB;
is the same as
package main;
use ModuleB;
use ModuleA;
and if
package ModuleA;
use ModuleB;
use ModuleC;
is the same as
package ModuleA;
use ModuleC;
use ModuleB;
etc.
EDIT:
Note to say that ModuleA loads ModuleC explicitly (and do not rely on ModuleB that it will load ModuleC) because ModuleA uses functions from ModuleC. Is this good design approach?
The best practice is easy: Each file, program or module, should specify all its dependencies. That's it. E.g., if a script needs modules A and B, and module A needs module B, don't count on module B already loaded by the script - what if some other script needs module A without needing B?
Good Exporter based modules should use #EXPORT_OK and you should explicitly list the imported subroutines in the use-clause. It helps to prevent name clashes.
For normal modules that only export subroutines, order shouldn't matter. In other cases, it might, though: consider
use warnings_;
use diagnostics;
versus
use diagnostics;
use warnings_;

"Undefined subroutine &HTML::Entities::decode_entities called"

I'm getting the error
Undefined subroutine &HTML::Entities::decode_entities called`
using LWP::UserAgent, although the module is there, as well as the HTML::Parser module.
I suspect it has something to do with XS modules missing, since the function in question seems to be implemented in XS, but I am at a loss.
Recent versions of HTML::Entities depend on getting the decode_entities routine by loading the HTML::Parser module's XS component. Since the two modules are distributed together, this should not be a problem, but it's possible you have an older HTML::Parser version that didn't use XS instead (or multiple versions of HTML::Parser installed, with the wrong one being found first).
Check the $VERSION in HTML::Parser, look up that distribution on http://search.cpan.org/dist/HTML-Parser, and verify that distribution has the version of HTML::Entities that you have.
Are you missing this line:
use HTML::Entities;
From the HTML::Entities CPAN page, it should be used like this:
use HTML::Entities;
my $a = "Våre norske tegn bør &#230res";
decode_entities($a);
encode_entities($a, "\200-\377");
If you think there is something wrong with the HTML::Entities package, you can check the source on your system. From bash:
vim $(perldoc -l HTML::Entities)
Once the file is opened in your text editor, you can check that the subroutine is defined. I suspect that the package is correct though, it is more likely that the package isn't "used".
You can also test this at the command line to see if it works outside your program:
perl -MHTML::Entities -le 'print HTML::Entities::decode_entities( "Våre norske tegn bør &#230res" )'