How do I import a perl module outside of #INC that does not end in .pm? - perl

Background
I am attempting to import a perl module that does not end in .pm with a method similar to this answer:
use lib "/hard/coded/directory"; use scripting;
However, when I attempt to import a module in this way, I get the following error when running perl -c:
Can't locate scripting.pm in #INC (#INC contains: ... ... ... /hard/coded/directory) at name of script line 47.
BEGIN failed--compilation aborted at name of script line 47.
Question
How do I import a perl module outside of #INC that does not have .pm at the end of the file?

If the file has a package directive, the file name and the package directive need to match, so simply fix the file name.
If the file doesn't have a package directive, you don't have a module, and you shouldn't use use or require. This can cause problems.
What you have is sometimes called a library, and you should use do.
do('/hard/coded/directory/scripting')
or die $# || $!;
(For proper error checking, the file needs to result in a true value.)
That said, you are probably trying to do something really awful. I'm guessing you're either have a configuration file written in Perl or a poorly written module. Perl is not a suitable choice of language for a configuration file, and avoiding namespaces is just bad programming with no benefit.

If the source file does not define new namespaces or classes and you just want to read the function definitions or data from a file, Perl provides the do and require functions.
do "scripting";
require "scripting";
The difference between them is that require will look for the file to evaluate to a true value (it expects the last statement in the file to resolve to a non-zero, non-empty value), and will emit a fatal error if this does not happen. (You will often see naked 1; statements at the end of modules to satisfy this requirement).
If scripting really contains class code and you do need all the functionality that the use function provides, remember that
use Foo::Bar qw(stuff);
is just syntactic sugar for
BEGIN {
$file = <find Foo/Bar.pm on #INC>;
require "$file";
Foo::Bar->import( qw(stuff) )
}
and suggests how you can workaround your inability to use use:
BEGIN {
require "scripting";
scripting->import()
}
In theory, the file scripting might define some other package and begin with a line like package Something::Else;. Then you would load the package in this module with
BEGIN {
require "scripting";
Something::Else->import();
}

Related

How to test missing optional perl modules via the command line perl call

I have a test suite with hundreds of tests for a perl module I've yet to release which is a command line interface. Since it is a command line interface, the tests (until possibly now) are all written to drop code into a template and then call the template script using a system call.
I recently added an optional dependency on a 3rd party module that's not a part of core perl. I know my module works whether that module is installed or not because I have a computer with it installed and one without and the module works without error in each case. However, I'd like to be able to write a test to confirm that my module will work when the 3rd party module is absent - and I'd like that test to work even if the 3rd party module is installed, but behave as if it wasn't.
Ideally, I could use the structure I've put in place for testing which makes a system call to a template script. I know I could write a separate test script that manipulates #INC in the BEGIN block, imports the particular methods that use the module, and call them like a unit test. But I would like to know if there's a way I can use the test structure I've already got all my other tests using, which is to make a system call.
So is there a way to exclude a module from being imported via a perl command line option? I've tried -M-Module, but the code use Module still imports the module.
Incidentally, my module uses the 3rd party module inside an eval, which is how I made it optional.
I wrote Test::Without::Module for this exact case. It works by modifying #INC to prevent loading of modules that you name. For testing, you could either run the test from the command line:
perl -MTest::Without::Module=Some::Module -w -Iblib/lib t/SomeModule.t
Or allow/disallow loading the module from within your test suite:
use Test::Without::Module qw( My::Module );
# Now, loading of My::Module fails :
eval { require My::Module; };
warn $# if $#;
# Now it works again
eval q{ no Test::Without::Module qw( My::Module ) };
eval { require My::Module; };
print "Found My::Module" unless $#;

Perl can't find module listed in same directory as script

I need to keep any custom modules in the same-ish directory as my script.
I keep getting this error suggesting that Perl can't "see" the module I'm declaring
Global symbol "$employment_type" requires explicit package name at early_enrollments_by_term.pl line 260.
Execution of early_enrollments_by_term.pl aborted due to compilation errors.
My module is Hash.pm and is in directory /home/pgb2/canvas/canvas-sis-feeds/scripts/lib/Custom
Hash.pm
package Custom::Hash;
My script is early_enrollments_by_term.pl and lives in /home/pgb2/canvas/canvas-sis-feeds/scripts/
early_enrollments_by_term.pl
#!/usr/bin/perl
use strict;
use DBI;
use lib qw(/home/pgb2/canvas/canvas-sis-feeds/scripts/lib);
use Custom::Hash;
my $enrollment_type = $Custom::Hash::enrollment_hash{$role}; # line 259
print "\nenrollment_type: $employment_type\n"; # line 260
Would anyone be able to tell why it's not finding the module?
Although you have shown very little of your code,
the problem would presumably be that you have declared $enrollment_type and tried to use $employment_type, which has not been declared
Had perl been unable to find the module, it is the use Custom::Hash statement that would throw the error

Check and report Perl module missing

Is there any way to report the missing modules used in the Perl file beforehand instead of getting as an error.
I have something like use Digest::MD5, use File::DosGlob modules in my Perl program. Whenever the users run the script they are getting an error if there is no specific module installed in their system. They could not understand the default error message given by #INC. So I would like to clearly tell them that these modules need to be installed to run the script.
You could build your own verification by using a BEGIN block. Those are run at compile time, just like use is. Keep in mind that use Foo is essentially nothing else as this:
BEGIN {
require Foo;
Foo->import;
}
The following code will replace all use statements with a single BEGIN and place them inside an eval. That's essentially like a try/catch mechanism.
We need the string eval (which is considered evil around here) because require only converts from package names with colons :: to paths if the argument is a bareword. But because we have the name in $module, it's a string, so we need to place it into an eval according to require's docs.
If that string eval fails, we die. That's caught by the outer eval block and $# is set. We can then check if it contains our module name, in which case we naively assume the failure was because that module is not installed. This check could be a bit more elaborate.
We keep track of any failures in $fails, and if there were any, we stop.
#!/usr/bin/perl
use strict;
use warnings;
# all our use statements go here
BEGIN {
my $fails;
foreach my $module ( qw/Digest::MD5 File::DosGlob ASDF/ ) {
eval {
eval "require $module" or die; # because $module is not a bareword
$module->import;
};
if ($# && $# =~ /$module/) {
warn "You need to install the $module module";
$fails++;
}
}
exit if $fails;
}
# ...
Above I included ASDF, which I don't have, so when run it will say
You need to install the ASDF module at /home/code/scratch.pl line 1335.
You might want to make that message a bit more verbose. If your users are not able to understand the default error message that Perl gives when it cannot find a module, it might be wise to include a guide on how to install stuff right there.
Note that both modules you listed have been included with Perl for a while (read: since March 2002). So why would you want to do this for those modules?
$ corelist Digest::MD5
Data for 2014-09-14
Digest::MD5 was first released with perl v5.7.3
$ corelist File::DosGlob
Data for 2014-09-14
File::DosGlob was first released with perl 5.00405
A better way would be ship your program as a distribution that can be installed, and include a Makefile or a cpanfile or something similar that lists dependencies. There is a guide in perlnewmod on how to start a new module. You'd not want to upload to CPAN obviously, but the basics are the same.
With this, your users would get all dependencies installed automatically.
You could use Devel::Modlist, it will list all the required module for your program.
perl -d:Modlist test.pl
There's another module Module::ScanDeps which comes with a utility scandeps.pl which you can use on your script as:
scandeps.pl test.pl
Note that sanity checking your Perl code using perl -c is dangerous, so use it carefully.
Your question isn't really clear about what "beforehand" means. To check if a Perl program's syntax is correct and directly included modules are resolvable, use
perl -c <perl-program.pl>
This checks the syntax of your file and ensures that any modules used by your code exist. However, it does not transitively check the entire dependency tree, only those mentioned in perl-program.pl.

Using a Perl module in my own directory [duplicate]

I have a module in the parent directory of my script and I would like to 'use' it.
If I do
use '../Foo.pm';
I get syntax errors.
I tried to do:
push #INC, '..';
use EPMS;
and .. apparently doesn't show up in #INC
I'm going crazy! What's wrong here?
use takes place at compile-time, so this would work:
BEGIN {push #INC, '..'}
use EPMS;
But the better solution is to use lib, which is a nicer way of writing the above:
use lib '..';
use EPMS;
In case you are running from a different directory, though, the use of FindBin is recommended:
use FindBin; # locate this script
use lib "$FindBin::RealBin/.."; # use the parent directory
use EPMS;
There are several ways you can modify #INC.
set PERL5LIB, as documented in perlrun
use the -I switch on the command line, also documented in perlrun. You can also apply this automatically with PERL5OPT, but just use PERL5LIB if you are going to do that.
use lib inside your program, although this is fragile since another person on a different machine might have it in a different directory.
Manually modify #INC, making sure you do that at compile time if you want to pull in a module with use. That's too much work though.
require the filename directly. While this is possible, it doesn't allow that filename to load files in the same directory. This would definitely raise eyebrows in a code review.
Personally I prefer to keep my modules (those that I write for myself or for systems I can control) in a certain directory, and also to place them in a subdirectory. As in:
/www/modules/MyMods/Foo.pm
/www/modules/MyMods/Bar.pm
And then where I use them:
use lib qw(/www/modules);
use MyMods::Foo;
use MyMods::Bar;
As an aside.. when it comes to pushing, I prefer the fat-arrow comma:
push #array => $pushee;
But that's just a matter of preference.
'use lib' is the answer, as #ephemient mentioned earlier. One other option is to use require/import instead of use. It means the module wouldn't be loaded at compile time, but instead in runtime.
That will allow you to modify #INC as you tried there, or you could pass require a path to the file instead of the module name. From 'perldoc -f require':
If EXPR is a bareword, the require assumes a ".pm" extension and
replaces "::" with "/" in the filename for you, to make it easy to
load standard modules. This form of loading of modules does not risk
altering your namespace.
You have to have the push processed before the use is -- and use is processed early. So, you'll need a BEGIN { push #INC, ".."; } to have a chance, I believe.
As reported by "perldoc -f use":
It is exactly equivalent to
BEGIN { require Module; import Module LIST; }
except that Module must be a bareword.
Putting that another way, "use" is equivalent to:
running at compile time,
converting the package name to a file name,
require-ing that file name, and
import-ing that package.
So, instead of calling use, you can call require and import inside a BEGIN block:
BEGIN {
require '../EPMS.pm';
EPMS->import();
}
And of course, if your module don't actually do any symbol exporting or other initialization when you call import, you can leave that line out:
BEGIN {
require '../EPMS.pm';
}
Some IDEs don't work correctly with 'use lib', the favored answer. I found 'use lib::relative' works with my IDE, JetBrains' WebStorm.
see POD for lib::relative
The reason it's not working is because what you're adding to #INC is relative to the current working directory in the command line rather than the script's directory.
For example, if you're currently in:
a/b/
And the script you're running has this URL:
a/b/modules/tests/test1.pl
BEGIN {
unshift(#INC, "..");
}
The above will mean that .. results in directory a/ rather than a/b/modules.
Either you must change .. to ./modules in your code or do a cd modules/tests in the command line before running the script again.

In Perl, what is the difference between a .pm (Perl module) and .pl (Perl script) file?

What is the Difference between .pm (Perl module) and .pl (Perl script) file?
Please also tell me why we return 1 from file. If return 2 or anything else, it's not generating any error, so why do we return 1 from Perl module?
At the very core, the file extension you use makes no difference as to how perl interprets those files.
However, putting modules in .pm files following a certain directory structure that follows the package name provides a convenience. So, if you have a module Example::Plot::FourD and you put it in a directory Example/Plot/FourD.pm in a path in your #INC, then use and require will do the right thing when given the package name as in use Example::Plot::FourD.
The file must return true as the last statement to indicate successful execution of any initialization code, so it's customary to end such a file with 1; unless you're sure it'll return true otherwise. But it's better just to put the 1;, in case you add more statements.
If EXPR is a bareword, the require assumes a ".pm" extension and replaces "::" with "/" in the filename for you, to make it easy to load standard modules. This form of loading of modules does not risk altering your namespace.
All use does is to figure out the filename from the package name provided, require it in a BEGIN block and invoke import on the package. There is nothing preventing you from not using use but taking those steps manually.
For example, below I put the Example::Plot::FourD package in a file called t.pl, loaded it in a script in file s.pl.
C:\Temp> cat t.pl
package Example::Plot::FourD;
use strict; use warnings;
sub new { bless {} => shift }
sub something { print "something\n" }
"Example::Plot::FourD"
C:\Temp> cat s.pl
#!/usr/bin/perl
use strict; use warnings;
BEGIN {
require 't.pl';
}
my $p = Example::Plot::FourD->new;
$p->something;
C:\Temp> s
something
This example shows that module files do not have to end in 1, any true value will do.
A .pl is a single script.
In .pm (Perl Module) you have functions that you can use from other Perl scripts:
A Perl module is a self-contained piece of Perl code that can be used by a Perl program or by other Perl modules. It is conceptually similar to a C link library, or a C++ class.