Modifications to local perl module not recognized - perl

I have a single perl subroutine in a file of its own. It is called separately by two different perl scripts. I have modified the list of arguments passed in, and made the necessary changes in both calling scripts, but only one script does what I expect. When I use the perl debugger on the other one, when the debugger steps into the subroutine in the module, it shows me the way the module looked before I edited it. So when I run that script, it is passing in the new argument list, but the module is trying to assign them the old way, which clearly is wrong.
What could cause this? Why does one script work correctly and the other one not?
For specifics, yes I'm using strict and warnings.
Old:
use strict;
use warnings;
use lib("/absolute/path/to/module"); #mymodule.pm lives here
use packagename;
my $string = "foo";
my %Hash = my_sub($foo);
and in the module:
package packagename;
use strict;
use warnings;
use Exporter;
use vars qw(#ISA #EXPORT #EXPORT_OK);
#ISA = qw( Exporter );
#EXPORT = qw(my_sub);
#EXPORT_OK = qw(my_sub);
sub my_sub {
my $string = $_[0];
my %Hash;
...
return %Hash;
}
I've added an array reference to the arguments. New to the calling script are:
my #array = qw(Zero One Two);
my %Hash = my_sub(\#array, $string);
New to the module:
sub my_sub {
my (#array) = #{$_[0]};
my $string = $_[1];
}
When I run my script with the debugger and step into this module, it shows my $string being assigned from $_[0] as the first line of the subroutine, just as though I never changed the module.
Again, this only happens with one of my scripts but the other one works fine. Everything is in the same directory. I'm running this on linux, perl 5.10. I'm baffled!

I would guess that the scripts are loading different copies of the module, and you only modified one copy. Try adding
print $INC{'packagename.pm'}."\n";
after the use packagename line in each script. (Don't forget to replace :: with / in packagename, if any.) That will tell you if the scripts are really loading the same module.

Related

Issues using perl modules and passing variables into subroutines

So I am new to the perl programing language and I want to get myself acquainted with creating, using, and passing parameters into modules. I made a hello world module that takes as a parameter two string variables from the main.pl testing program, one that says "hello" and another that says "world" and prints them out. Every time I try running main.pl I keep getting errors and I have spent many days trying to get this otherwise simple program to function correctly.
This is the code for my main.pl function:
use FindBin;
use lib $FindBin::Bin;
use test;
use strict;
my $firststring = "hello";
my $secondstring = "world";
test::printthing(\$firststring, \$secondstring);
And this is the coded for my test.pm module:
package test;
use strict
use Exporter;
our #ISA = qw/Exporter/;
our #EXPORT = qw/&main/;
sub printthing{
my $firstword = $_[0];
my $secondwork = $_[1];
print"$firstword\n";
print"$secondword\n";
}1;
You're missing the semicolon from the end of your use strict line in the module.
You try to export the main() subroutine, but your module doesn't have a subroutine called main().
You pass references to your variables to the subroutine but don't dereference them before printing them.
For the final point, you can either continue to pass in references, but dereference before printing.
test::printthing(\$firststring, \$secondstring);
# and then in the subroutine...
print"$$firstword\n";
print"$$secondword\n";
Or, you could just pass in the variables and ignore references completely.
test::printthing($firststring, $secondstring);
# and then in the subroutine...
print"$firstword\n";
print"$secondword\n";

"Can't locate object method "new" [...]" in Perl using the AI::Pathfinding::AStar CPAN module

This is the first "serious" thing I'm doing in Perl, so please pardon me if the question is somewhat silly.
I wanted to play around with the A* pathfinding algorithm. I found the AI::Pathfinding::AStar CPAN Module and am basically trying to get the given example to work.
First of all I separated the example into two files, because I couldn't figure out how to make the use My::Map::Package; work with everything in a single file. I came up with the following two files:
MyAstar.pm:
package MyAstar;
use warnings;
use strict;
use base "AI::Pathfinding::AStar";
my %NODES = get_all_nodes();
sub get_all_nodes {...}
sub getSurrounding {...}
main.pl:
#!/usr/bin/env perl
package main;
use lib '/home/foo/astar/';
use warnings;
use strict;
use MyAstar;
my $map = MyAstar->new or die "No map for you!";
my $path = $map->findPath(1, 5);
print join(', ', #$path), "\n";
When I execute main.pl I get the following error:
Can't locate object method "new" via package "MyAstar" at main.pl line 9.
I'm not sure what the problem is here. I would have expected, there to be a subroutine by the name new in the AI::Pathfinding::AStar package, but couldn't find it. Is the CPAN Module broken or am I doing something wrong?
You try to call a function (MyAstar->new, which conventionally is used as a constructor), but you don't define it. There is no default constructor in Perl (like in e.g., Java).
Add something like this to your MyAstar.pm:
sub new {
my $class = shift;
my $self = bless{}, $class;
# initialize $self here as needed, maybe using any passed arguments in #_
return $self;
}

Importing variable into Perl package

I'm writing a basic program whose core logic is split across several project-specific modules for cleanliness (keeping subroutines organised by their purpose in the program's logic).
Suddenly had trouble exposing an option from the main package in one of the modules, and using the our statement appeared to have no effect.
For brevity, I'll copy+paste an isolated test case I wrote to examine this behaviour:
main.pl
#!/usr/bin/perl
use warnings;
use strict;
use File::Basename;
# The variable to be read by the module.
our $verbose = 1;
# Load Output.pm from directory
use lib dirname "$0";
use Output;
write_message "Hello, world\n";
Output.pm
package Output;
use warnings;
use strict;
use parent "Exporter";
our #EXPORT = qw(write_message);
# Should be imported?
our $verbose;
sub write_message {
print $_[0] unless !$verbose;
}
1;
Expected result: "Hello, world"
Actual result: Dead silence
It's quite possible that what I'm trying to achieve isn't even possible in Perl, as this isn't the intended use of modules (and heck, I understand why that'd be the case).
I'm still quite new to Perl and there are some things I'm struggling to wrap my head around. I've seen people recommend using the our declarator to expose a variable across packages, but I can't understand why this isn't working.
PS: If anybody knows a better approach to splitting an app's program-specific logic between modules, I'd appreciate some pointers too. :) But first and foremost, I'd prefer to learn why our-ing a variable isn't working.
An our statement just creates a package variable (whereas my creates a lexical variable). It has nothing to do with exporting
The best option is probably to declare the variable in the Output package and access it as $Output::verbose elsewhere. Like this
main.pl
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
use lib dirname $0;
use Output;
$Output::verbose = 1;
write_message "Hello, world\n";
Output.pm
package Output;
use strict;
use warnings;
use Exporter 5.57 'import';
our #EXPORT = qw/ write_message /;
our $verbose;
sub write_message {
print $_[0] if $verbose;
}
1;
Note that I have also removed the incorrect quotes from around $0, and ever since version 5.57 of Exporter it has been possible (and preferable) to import it's import subroutine instead of subclassing it
our declares a package variable in the current package. The one in main.pl refers to $main::verbose; the one in Output.pm refers to $Output::verbose.
You can use the full name $main::verbose to access the variable from anywhere, but you can't really "export" it because exporting refers to making symbols accessible to users of your module. You're trying to do the opposite.

imported perl variable imports with no value

SOLVED: As it turns out, my problem was rooted in the fact that I was not putting a $ in front of DEBUGVAR in the #EXPORT_OK assignment and the "use config_global qw(config DEBUGVAR);" line. Since it raises no error, I had no way to know this was the issue. So, the fix is to place the proper syntax in front of your variables at these points.
So I am trying to get the hang of writing and importing perl modules. I don't know why it was made so difficult to do this, but I am having a great deal of trouble with this seeimingly trivial task. Here is the contents of my module:
package global_config;
use strict;
require Exporter;
our #ISA = qw(Exporter);
our #EXPORT_OK = qw(DEBUGVAR);
our ($DEBUGVAR);
our $DEBUGVAR = "Hello, World!";
return 1;
Here are the contents of my perl script that imports the module:
use strict;
use config_global qw(config, DEBUGVAR);
our %config;
our $DEBUGVAR;
print "variable imported with value: ".$DEBUGVAR;
The output is "variable imported with value:", and nothing else. My variable appears to be losing it's value. What am I doing wrong?
EDIT: After fiddling around a bit, and turning warnings on, I have isolated the issue to being that $DEBUGVAR is never actually imported. When I use it via $config_global:DEBUGVAR, it works as expected. The issue now is that it is not importing into the namespace. What gives?
I see several issues:
You should not use a comma in the qw() syntax. The qw takes each whitespace separated phrase and puts it in an array element.
These two are the same:
my #bar = qw(foo bar barfu); #No commas!
my #bar = ("foo", "bar", "barfu"); #Commas Required
If you're exporting a variable, you need to put the sigil in front of it.
You have:
our #EXPORT_OK = qw(DEBUGVAR);
It should be:
our #EXPORT_OK = qw($DEBUGVAR);
You should use the newer Exporter syntax:
Here's the newer Exporter Syntax:
package global_config;
use strict;
use warnings;
use Exporter 'import'; #Not "require". No need for "#ISA"
our #EXPORT_OK = qw(DEBUGVAR);
our $DEBUGVAR = "Hello, World";
1; #Makes no real difference, but you shouldn't say "return 1". Just standard.
Finally, what are you doing exporting variables? That's just a bad practice.
Exporting anything is now questioned -- even functions. It pollutes the user's namespace. (At least you're using #EXPORT_OKAY). Take a look at File::Spec. It uses fully qualified package names for its subroutines by default.
The variable in question is accessible via the full package name $global_config::DEBUGVAR, so there's no real need to export it.
What if everybody did it? Yes, you last heard of this excuse in kindergarten, but it applies here. Imagine if several modules exported $DEBUGVAR.
There are several ways around your quandary, but the best is to use object oriented Perl to help set this variable, and even allow users to change it.
package MyPackage;
use strict;
use warnings;
use feature qw(say);
sub new {
my $class = shift;
my $debug = shift; #Optional Debug Value
my $self = {};
bless $self, $class;
if (not defined $debug) {
$debug = "Hello, world!";
$self->Debug($debug);
return $self;
}
sub Debug {
my $self = shift;
my $debug = shift;
if (defined $debug) {
$self->{DEBUG} = $debug;
}
return $debug;
}
1;
To use this module, I simply create a new object, and Debug will be set for me:
use strict;
use warnings;
use MyPackage #No exporting needed
#Create an object w/ Debug value;
my $obj = MyPackage->new; #Debug is the default.
say $obj->Debug; #Prints "Hello, world!"
# Change the value of Debug
$obj->Debug("Foo!");
say $obj->Debug; #Now prints "Foo!"
#Create a new object with a different default debug
$obj2 = MyPackage->new("Bar!");
say $obj2->Debug; #Print "Bar!";
This solves several issues:
It allows multiple values of debug because each object now has its own values
There is no worry about namespace pollution or accessing package variables. Again, all you need is contained in the object itself.
It's easier to debug issues since the complexity is hidden inside the objects themselves.
It's the new preferred method, so you might as well get use to the syntax and be able to read object oriented Perl code. You'll be seeing it more and more.
While exporting variables from a package isn't necessarily a recommended practice, to do so, you need to use the actual name of the variable you are exporting. In this case it is $DEBUGVAR and not DEBUGVAR which would be the name of a subroutine.
In the script using the config module, you do not need to declare the $DEBUGVAR variable as our, since imported variables are exempt from strict vars.
You have gotten the name mixed up, it looks like:
use config_global ...
package global_config;
Though one would think that would issue warnings. Unless you are not using warnings...?
ETA:
our #EXPORT_OK = qw($DEBUGVAR);
^
Also, you have two declarations on that variable. You really need to use warnings when debugging, otherwise, you'll never get anywhere.
Are you sure you want a comma here:
use config_global qw(config, DEBUGVAR);
Also, you aren't exporting config, so it might work better as:
use config_global qw(DEBUGVAR);
I'd also remove the last our $DEBUGVAR; since it might set it to undef (or at least put it before the "use" line) -- I am not sure about this though.

Why can't my Perl script see the our() variables I defined in another file?

I have a question relating to Perl and scoping. I have a common file with lots of various variables. I require the common file in my main script, but I cannot access the variables; they seem to be outside of its scope. I assumed that an our declaration would overcome that problem, but it doesn't seem to work.
Script 1: common.pl
#!/usr/bin/perl
our $var1 = "something";
our $var2 = "somethingelse";
Script 2: ftp.pl
#!/usr/bin/perl
use strict;
use warnings;
require('common.pl');
print $var1;
I get the error: Global symbol "$var1" requires explicit package name
There's no require statement in your second example, but it wouldn't work anyway. What our does is declare a lexically-scoped package variable. Since you have no package statement, it uses the default package main. So your first script sets up the variable $main::var1, but this will only be available within that file's scope.
A better way to provide common variables for other scripts is to use Exporter. You can define package symbols in one place and Exporter will take care of copying them to the requesting script or class's namespace when needed.
I would put the config in a module instead.
File: MyConfig.pm
package MyConfig;
require Exporter;
use strict;
our #ISA = qw(Exporter);
our #EXPORT = qw( getconfig );
my %confighash = (
thisone => 'one',
thatone => 2,
somthingelse => 'froboz',
);
sub getconfig {
return %confighash;
}
1;
Example usage:
#!/usr/bin/perl
use strict;
use warnings;
use MyConfig;
my %config = getconfig();
print $config{ somthingelse };
This should print froboz
It looks like you need a proper configuration file there. I'd go for a non-code configuration file that you can read when you need to setup things. There are modules on CPAN to handle just about any configuration format you can imagine.
If you want to do it the way you have it, get rid of our and declare them with use vars. Don't let the PBP police scare you off that. :) You only really need our to limit a scope of a package variable, and that's exactly the opposite of what you are trying to do.
our() does something a little different than you think. Its sole purpose is to work with strict in requiring you to declare package variables that you are going to use (unless they are fully-qualified or imported). Like strict, its effect is lexically-scoped. To use it to allow accessing a global $main:var1 from multiple files (which are separate scopes) as just $var1, you need to say our $var1 in each file.
Alternatively, you would change your required file to be a module with its own package that exports the variables to any package that uses it.
Try this. I am new at Perl but this is how I got it to work on a script I made
#!/usr/bin/perl
$var1 = "something";
$var2 = "somethingelse";
Script 2: ftp.pl
#!/usr/bin/perl
use strict;
use warnings;
our $var1;
our $var2;
require('common.pl');
print $var1;