in reply to Re: "use" modules inside modules
in thread "use" modules inside modules

Show us some code!

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

Replies are listed 'Best First'.
Re^3: "use" modules inside modules
by bogaertb (Novice) on Jun 28, 2009 at 16:39 UTC
    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".

      First, let's simplify your code a bit.

      In the .pl file, replace

      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"); }
      with
      use Cwd qw( realpath ); use File::Basename; my $script_dir; BEGIN { $script_dir = dirname(realpath($0)); } use lib "$script_dir/lib", "$script_dir/lib-perl";

      In the .pm files, remove the following

      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"); }

      It makes no sense to tell perl where to find the library after it's already been found!


      Now on to your question.

      To determine the presence of an element I use the statement defined($ntCapabilities{$type}{'SW_PAR'}).

      From where? The .pl file? There is no such variable there. You would have to export %ntCapabilities in order to do that (and roll out your own import since no module will help you export a lexical variable).

      But since exporting variables (as oppose to functions) is rather fishy, it would be better to create an accessor (sub is_capable?) and export the accessor from Capability or use its full name.

      if (Capability::is_capable(...)) { ... }
        Thanks for the cleanup tip. Now on your question: the call defined($ntCapabilities{$type}{'SW_PAR'} is only done in Capability.pm, nowhere else.

      $ntCapabitilies needs to be declared using our if you want to share its data between modules. The function is working presumably because it was defined in the same namespace as $ntCapabilities and hence can see its data. The accessor method is in a different namespace (Octopus?) and so can't see its data, even if you import the name. into the other namespace. This is because the imported variable is a package ("our") variable rather than a lexically scoped ("my") variable. All your values were assigned to the "my" variable, not the "our" variable.

      Two other points:

      One really should use warnings, not just strict. If you don't like all the warnings that use warnings; spews out, then clean up the code that is causing them. Getting rid of it to quiet the warnings is only going to cause you a world of trouble. If you have a very specific reason to do something normally warned about (e.g. you need to munge the symbol table), then turn off a specific warning for a specific block. Turning of warnings globally is a bad idea. Someone is paying you a lot of money to develop this code. It is important to give yourself every chance possible to catch mistakes.

      You and your team could have done a much better job of reducing this code to the essentials than you did. For example,

      • you could remove all data from $ntCapabilities except one or two elements needed to show the problem.
      • you could remove all "use" and "require" statements except the ones causing the problem. We don't have your family of modules and can't reliably run your code to see how it works on our system if you leave those extra "use" and "require" statements in.

      Best, beth

        I've worked in environments (including the current one) whereby, entirely due to legacy reasons, cleaning up the code from a warning(s) POV, is not feasible with in the constraints of the project at hand - indeed, it would require a self-contained project to totally clean up the code.

        The end result being that, wheresoever legacy code had/has to be used (pun intended) from within well-behaved code, it was/is done within a local block in order to circumvent the multifarious warnings that would otherwise be generated i.e.

        { no warnings; use Legacy::Module; }
        The removal of the warnings from the legacy code can then be done in piece-meal fashion as a background task without compromising the aggressive timescales for the new code.

        A user level that continues to overstate my experience :-))
        Hi, Thanks, but all scrips contain the shebang
        #!/usr/bin/perl -w
        Isn't that the same as use warnings?