bogaertb has asked for the wisdom of the Perl Monks concerning the following question:

I have a perl program using several modules. One of these modules say 'A' contains a data-hash being initialized with values (and is intended to remain some kind of constant data structure). The program also uses another module, say B. Module B itself also includes a "use A" call (to access a function from that module). I now have the funny thing that when I run the program it turns out that for some reason data in the hash of module A no longer seems to be initialized. If I remove the "use A" call in module B the program runs fine. I've been googling about using modules (containing data) in modules but did not find a lot (of my query was not too well...). Does someone have an idea how this behaviour is caused and how to get around this? Thanks.

Replies are listed 'Best First'.
Re: "use" modules inside modules
by wfsp (Abbot) on Jun 25, 2009 at 10:50 UTC
    And it all starts to get a bit circular! The image of a dog chasing its own tail comes to mind.:-)

    One way to break the chaos is to identify the common code used by both and put it in its own module, say, Common.pm. This could contain the "data-hash" and any functions needed by both A.pm and B.pm. A.pm and B.pm would then each have use Common;. Hopefully the dog would have one more turn and then lie down.

Re: "use" modules inside modules
by ELISHEVA (Prior) on Jun 25, 2009 at 11:20 UTC

    Are there any assignments to this variable in module B? Are you using strictures (use strict; use warnings;) in module B? Do module A, B and the main program have package declarations? If so, what are they?

    Perhaps module B is making an assignment to this hash? When it uses module A, it imports the variable from module A and overwrites its value. Then your main program sees the reassigned value. When it doesn't use module A, then it assigns the value to a variable in its own namespace instead of to the shared variable. Since the main program is using module A's version of the variable, it never sees the reassignment.

    This kind of thing would be pretty obvious if you are using strictures. When you removed "use A" you would get complaints about an undeclared variable. But if you are not, you won't get such complaints.

    But this is just a guess. For a better answer from me or others, I would recommend that you make a copy of all three modules and bit by bit remove code until you find the fewest lines of code needed to reproduce the problem. Then update your post with either the answer (if you nailed the reason for this behavior) or some sample code (if you did not).

    Best, beth

Re: "use" modules inside modules
by Jenda (Abbot) on Jun 25, 2009 at 11:09 UTC

    Where do you do the initialization? In the import() subroutine or outside any subroutines in the module? And are you sure it's not being initialized? How?

    Show us some code!

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

      Show us some code!

      Show us sufficient code to demonstrate the problem, but no more than that.

        Thanks for the feedback. Here we go with the code.
        "Main program" #!/usr/bin/perl -w ... # docu and version info -- skipped use strict; use FileHandle; use File::stat; use Getopt::Long; use POSIX ":sys_wait_h"; use POSIX qw(ceil); use Socket; #===================================================================== +========= #== Find the lib dir use Cwd; use File::Basename; BEGIN { my $dir = dirname($0); my $path; if ($dir =~ /^\//) { #--absolute path $path = $dir; } elsif ($dir eq ".") { #-- relative, current dir $path = getcwd(); } else { #-- relative, but not from current dir $dir =~ s/^\.\///; $path = getcwd()."/$dir"; } unshift(@INC, "$path/lib"); unshift(@INC, "$path/lib-perl"); unshift(@INC, "$path/lib-perl/sun4-solaris-64int"); } #===================================================================== +========= use Net::TFTP; use Net::SNMP::Security::USM v3.0.0; use IsamCli; use Isam7354RUCli; use Crypt::Lite; use Utils; # -- BB -- use Octopus; use Capability; use ToolCommon qw(:CONSTANTS); use PbmtConfig; use ErrorCodes qw(:EXIT_CODES :SWDL); use FamilyCommon; ....
        Here we see the (among others):
        use Octopus; use Capability;
        This is the Octopus.pm module (at least a the beginning):
        package Octopus; use strict; use File::Basename; use Cwd; BEGIN { my $dir = dirname($0); my $path; if ($dir =~ /^\//) { #--absolute path $path = $dir; } elsif ($dir eq ".") { #-- relative, current dir $path = getcwd(); } else { #-- relative, but not from current dir $dir =~ s/^\.\///; $path = getcwd()."/$dir"; } unshift(@INC, "$path/lib"); unshift(@INC, "$path/lib-perl"); } require Utils; require Capability; use ErrorCodes qw(:EXIT_CODES); use ToolCommon qw(:CONSTANTS); require ReduceSWP; ...
        So here again we have the usage of the module Capability because this module contains functions that logically belong there (it is a module having capabilities of elements it is dealing with). Now we turn to the Capability module. Half of this module is data (intented to be static). The rest of the module are accessor functions to access the data elements to prevent "direct" usage of the hashes defined inside but rather retrieve the data through the accessor functions. We are not using the Exporter (I tried it but it made no difference).
        package Capability; use strict; use Cwd; use File::Basename; BEGIN { my $dir = dirname($0); my $path; if ($dir =~ /^\//) { #--absolute path $path = $dir; } elsif ($dir eq ".") { #-- relative, current dir $path = getcwd(); } else { #-- relative, but not from current dir $dir =~ s/^\.\///; $path = getcwd()."/$dir"; } unshift(@INC, "$path/lib"); unshift(@INC, "$path/lib-perl"); } use IsamCli; use ToolCommon qw(:CONSTANTS); use ErrorCodes qw(:EXIT_CODES); #===================================================================== +========= #== Prototypes #===================================================================== +========= sub GetVersion(); sub GetError(); ... my %ntCapabilities = ('EANT-A' => {'SW_PAR' => undef}, 'EBNT-A' => {'SW_PAR' => undef}, 'ECNT-A' => {'SW_PAR' => $ECNTA_MINSWPARSIZE, 'PREFIX' => {'ALL' => 'L5YY'}, 'BRDCODE' => 12355, 'SAFE_ICS' => 13, 'SHUB_TYPE' => 'ECNTA', 'EQPT_TYPE' => $XD, 'IF_BASE' => $OTHER_IFBASE, 'FAMILY' => {'ALL' => $ISAM, '400 410' => $ISAM_ANSI}, 'NO_UPG' => {'240' => [$IMPOSSIBLE, $FTP_ISSUE], '241' => ['248 249 244 246', $INCOMP_RELSTREAM +], '242' => ['248 249 244 246', $INCOMP_RELSTREAM +]}, 'NO_MIG' => {'117' => [$IMPOSSIBLE, 'No migration possible from this release +'], # No migrations for this release '233' => ['241 242 243 248 249 244 246 250', 'Feature incompatibilty w.r.t. pppoe rel +ay tagging'], # No migrations due to pppoe relay tags of 2.3.03 '240' => [$IMPOSSIBLE, 'No upgrade possible due to file transfe +r problem'], '241' => ['248 249 244 246 300', $INCOMP_RELST +REAM], '242' => ['248 249 244 246 300', $INCOMP_RELST +REAM], '244' => ['250 300 310 320', $INCOMP_RELSTREAM +], '245' => ['250', $NO_MIG_PATH ], '246' => ['250 300 310 320', $INCOMP_RELSTREAM +], '248' => ['250', $INCOMP_RELSTREAM], '249' => ['250', $INCOMP_RELSTREAM], '250' => ['300', $INCOMP_RELSTREAM]}, 'MIG_EXCLUDED_TO' => {$ISAM => "330 340 400"} }, .... );
        The problem I have is occuring on this hash. To determine the presence of an element I use the statement
        defined($ntCapabilities{$type}{'SW_PAR'}).
        When I remove the call require Capability.pm from Octopus.pm and replace the function call by the code executed by the function the program is running fine, if the require statement is present and I use the accessor function, the program fails for exactly the same element that was OK when the require was removed. It is the intention that the data hash remains "local" to Capability.pm. I hope this is sufficient code allowing a "judgement".
Re: "use" modules inside modules
by whakka (Hermit) on Jun 25, 2009 at 23:33 UTC
    It could be that Module B is importing the hash from A (probably incidentally) and then altering it via a namespace collision. If this is the case don't export the hash by default in A, or don't import the hash into B. If this isn't the case it would be helpful to see code.