Including a module from another module [duplicate] - perl

I don't know how to do one thing in Perl and I feel I am doing something fundamentally wrong.
I am doing a larger project, so I split the task into different modules. I put the modules into the project directory, in the "modules/" subdirectory, and added this directory to PERL5LIB and PERLLIB.
All of these modules use some configuration, saved in external file in the main project directory - "../configure.yaml" if you look at it from the module file perspective.
But, right now, when I use module through "use", all relative paths in the module are taken as from the current directory of the script using these modules, not from the directory of the module itself. Not even when I use FindBin or anything.
How do I load a file, relative from the module path? Is that even possible / advisable?

Perl stores where modules are loaded from in the %INC hash. You can load things relative to that:
package Module::Foo;
use File::Spec;
use strict;
use warnings;
my ($volume, $directory) = File::Spec->splitpath( $INC{'Module/Foo.pm'} );
my $config_file = File::Spec->catpath( $volume, $directory, '../configure.yaml' );
%INC's keys are based on a strict translation of :: to / with .pm appended, even on
Windows, VMS, etc.
Note that the values in %INC may be relative to the current directory if you put relative directories in #INC, so be careful if you change directories between the require/use and checking %INC.

The global %INC table contains an entry for every module you have use'd or require'd, associated with the place that Perl found that module.
use YAML;
print $INC{"YAML.pm"};
>> /usr/lib/perl5/site_perl/5.8/YAML.pm
Is that more helpful?

There's a module called File::ShareDir that exists to solve this problem. You were on the right track trying FindBin, but FindBin always finds the running program, not the module that's using it. ShareDir does something quite similar to ysth's solution, except wrapped up in a nice interface.
Usage is as simple as
my $filename = File::ShareDir::module_file(__PACKAGE__,
'my/data.txt');
# and then open $filename or whatever else.
or
my $dirname = File::ShareDir::module_dir(__PACKAGE__);
# Play ball!

Change your use Module call to require Module (or require Module; Module->import(LIST)). Then use the debugger to step through the module loading process and see where Perl thinks it is loading the files from.

Related

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.

I want to create a word count module and I want to reuse it further

I want to create a module in Perl. The below code is not working properly. I want to create a word count module and I want to reuse it further. Can anyone help me out to create this module? This is my first attempt to create a module so kindly help me out.
package My::count
use Exporter qw(import);
our #Export_ok = qw(line_count);
sub line_count {
my $line = #_;
return $line;
}
I saved the above code in count.pm
use My::count qw(line_count);
open INPUT, "<filename.txt";
$line++;
print line count is $line \n";
I saved the above script in .pi extension.
This code is showing error when I run it on an Ubuntu platform. Kindly help me to fix this errors.
Perl scripts are stored with .pl extension. As you say use My::count qw(line_count); Perl tries to search the modules from the directories stored in #INC variable. You can run it with the -I flag to specify the directory to search the custom packages. Refer to this question for more info.
By convention Perl packages usually have a capitalized first letter, so My::count is more in keeping with convention if you call it package My::Count;. Typically lower-cased module names are reserved for pragmas such as 'strict' and 'warnings'. So go ahead and change the name to My::Count.
Next, save the module in a path such as lib/My/Count.pm. lib is by convention as well.
Then you have to tell your script where to find package My::Count.
Let's assume you're storing your module and your executable like this:
~/project/lib/My/Count.pm
~/project/bin/count.pl
Notice I also used a .pl extension for the executable. This is another convention. Often on Unix-like systems people omit the .pl extension altogether.
Finally, in your count.pl file you need to tell perl where to find the library. Often that is done like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/../lib";
use My::Count 'line_count';
# The rest goes here...
As you can see, we're using FindBin to locate where the executable is stored, and then telling perl that it should look (among other places) in the lib folder stored in a relative location to the executable.
Naturally, as this is Perl, this is not the only way to do it. But it's one common idiom.
You need to move your count.pm file into a directory called My. So you have the following.
./count.pl
./My/count.pm

perl get the path of a module inside the module?

I have a perl module /x/y/z/test.pm. Inside this module, I want to read a config file /x/y/z/test.config. Yet, I am including my module from /a/b/c/mymain.pl. How can I get /x/y/z/ to build the path for /x/y/z/test.config in /x/y/z/test.pm?
Thanks,
AFAIK FindBin will show mymain.pl (and it might have been used in other modules, then the first invocation will win). Try __FILE__:
my $path = __FILE__;
$path =~ s/pm$/config/;

A proper way of using Perl custom modules inside of other Perl modules

I'm using custom modules in my scripts and have to store them outside of Perl lib directory. So in Perl scripts (*.pl) I use the following block to include them in #INC:
BEGIN {
use FindBin qw($Bin);
push #INC, "$Bin/../ModulesFolder1";
push #INC, "$Bin/../ModulesFolder2";
}
But I also have to use modules inside of my other Perl modules (*.pm), and as I understand FindBin works for scripts only. So I change that block to:
BEGIN {
push #INC, catdir( dirname( $INC{'ThisModule.pm'} ), qw( .. ModulesFolder1 ) );
push #INC, catdir( dirname( $INC{'ThisModule.pm'} ), qw( .. ModulesFolder2 ) );
}
It works but with a little problem. I code in Eclipse with EPIC plugin, and "if you have something in a BEGIN block that causes the compiler to abort prematurely, it won't report syntax errors to EPIC", so that way I loose Perl syntax check in modules.
So, with FindBin (in scripts) I don't have to use any functions (like catdir) in BEGIN{} block, and the syntax check of the following code goes on correctly. Besides, I'd like not to change any environment variables (like PERL5LIB), so that I could use the scripts on my colleagues' machines without any additional preparations.
What's the proper way of using custom Perl modules inside of other modules, and not interfering with EPIC syntax check at the same time? Or maybe I even should include modules in completely other way?
I strongly disagree with modifying #INC in modules. It causes all kinds of headaches. Let the script (or even the calling process via the PERL5LIB environment variable) setup #INC correctly.
script.pl:
use FindBin qw( $RealBin );
use lib
"$RealBin/../ModulesFolder1",
"$RealBin/../ModulesFolder2";
use ModuleInFolder1;
ModuleInFolder1.pm:
use ModuleInFolder2; # Works fine.
As for EPIC, do the following:
Right-click on the project.
Properties
Perl Include Path
${project_loc}/ModulesFolder1, Add to list
${project_loc}/ModulesFolder2, Add to list
(I literally mean the 14 chars ${project_loc}. That means something to EPIC. It will continue to work even if you move the project.)
PS — $RealBin is better than $Bin because it allows you to use a symlink to your script.
PS — __FILE__ is more appropriate than $INC{'ThisModule.pm'}.
Not sure about Eclipse, but you can use use lib (will probably not work, it changes #INC at compile time) or set the environment variable PERL5LIB to point to your library folder(s).
Set up PERL5LIB environment variable. Every time you use or require, Perl will check all directories listed in it.
Alternatively, place all necessary custom modules under script's directory, so you can use relative paths in use lib. It will also allow you to quickly make a bundle to transfer everything to another PC by just packing recursively from top-level directory.
Another solution (from my colleague) - a change to be made in the module:
sub path_to_current_module() {
my $package_name = __PACKAGE__ .'.pm';
$package_name =~ s#::#/#g;
for my $path ( #INC ) {
# print "\$path == '$path'\n";
if ( -e catfile( $path, $package_name ) ) {
return $path;
}
}
confess;
}
BEGIN {
my $path_to_current_module = path_to_current_module();
push #INC, catdir( $path_to_current_module, qw( .. ModulesFolder1 ) );
push #INC, catdir( $path_to_current_module, qw( .. ModulesFolder2 ) );
}
It seems that the old way (described in the question) Perl couldn't locate current module name in #INC - that's why perl -c was interrupted by error inside of the BEGIN block. And the described sub helps it to determine the real path to the current module. Besides, it doesn't depend on the current file name and can be copied to another module.

How do I load libraries relative to the script location in Perl?

How can you get current script directory in Perl?
This has to work even if the script is imported from another script (require).
This is not the current directory
Example:
#/aaa/foo.pl
require "../bbb/foo.pl"
#/bbb/bar.pl
# I want to obtain my directory (`/bbb/`)
print($mydir)
The script foo.pl could be executed in any ways and from any directory, like perl /aaa/foo.pl, or ./foo.pl.
What people usually do is
use FindBin '$Bin';
and then use $Bin as the base-directory of the running script. However, this won't work if you do things like
do '/some/other/file.pl';
and then expect $Bin to contain /some/other/ within that. I'm sure someone thought of something incredibly clever to work this around and you'll find it on CPAN somewhere, but a better approach might be to not include a program within a program, but to use Perl's wonderful ways of code-reuse that are much nicer than do and similar constructs. Modules, for example.
Those generally shouldn't care about what directory they were loaded from. If they really need to operate on some path, you can just pass that path to them.
See Dir::Self CPAN module. This adds pseudo-constant __DIR__ to compliment __FILE__ & __LINE__.
use Dir::Self;
use lib __DIR__ . '/lib';
I use this snippet very often:
use Cwd qw(realpath);
use File::Basename;
my $cwd = dirname(realpath($0));
This will give you the real path to the directory containing the currently running script. "real path" means all symlinks, "." and ".." resolved.
Sorry for the other 4 responses but none of them worked, here is a solution that really works.
In below example that adds the lib directory to include path the $dirname will contain the path to the current script. This will work even if this script is included using require from another directory.
BEGIN {
use File::Spec;
use File::Basename;
$dirname = dirname(File::Spec->rel2abs( __FILE__ )) . "/lib/";
}
use lib $dirname;
From perlfaq8's answer to How do I add the directory my program lives in to the module/library search path?
(contributed by brian d foy)
If you know the directory already, you can add it to #INC as you would for any other directory. You might if you know the directory at compile time:
use lib $directory;
The trick in this task is to find the directory. Before your script does anything else (such as a chdir), you can get the current working directory with the Cwd module, which comes with Perl:
BEGIN {
use Cwd;
our $directory = cwd;
}
use lib $directory;
You can do a similar thing with the value of $0, which holds the script name. That might hold a relative path, but rel2abs can turn it into an absolute path. Once you have the
BEGIN {
use File::Spec::Functions qw(rel2abs);
use File::Basename qw(dirname);
my $path = rel2abs( $0 );
our $directory = dirname( $path );
}
use lib $directory;
The FindBin module, which comes with Perl, might work. It finds the directory of the currently running script and puts it in $Bin, which you can then use to construct the right library path:
use FindBin qw($Bin);
You can also use local::lib to do much of the same thing. Install modules using local::lib's settings then use the module in your program:
use local::lib; # sets up a local lib at ~/perl5
See the local::lib documentation for more details.
Let's say you're looking for script.pl. You may be running it, or you may have included it. You don't know. So it either lies in the %INC table in the first case or as $PROGRAM_NAME (aka $0) in the second.
use strict;
use warnings;
use English qw<$PROGRAM_NAME>;
use File::Basename qw<dirname>;
use File::Spec;
use List::Util qw<first>;
# Here we get the first entry that ends with 'script.pl'
my $key = first { defined && m/\bscript\.pl$/ } keys %INC, $PROGRAM_NAME;
die "Could not find script.pl!" unless $key;
# Here we get the absolute path of the indicated path.
print File::Spec->rel2abs( dirname( $INC{ $key } || $key )), "\n";
Link to File::Basename, File::Spec, and List::Util