Obscure baffling "module not exported" error: can someone help mefind the cause?

H

Henry Law

I have a bizarre problem with packages and I'm hoping that someone can
help me find out what I'm doing wrong because I'm utterly stumped.

The error is "not exported" for something that quite clearly is exported
(details follow). The error disappears when one of several particular
lines is deleted or commented out, one of which refers to a different
Perl module altogether, not used anywhere in any of the code except for
the "use" statement. And yet that completely unused module is specific
to the error: simply replacing it with another, equally unused, module
causes the error to disappear. Again, more details below.

It's the most perplexing thing I've seen in all my years of debugging
hardware and software systems and I'm forced to the conclusion that
there's some corruption in the Perl installation itself, especially
since the code compiles clean on another machine at ostensibly the same
level (though the offending machine is newly-built). If someone can even
point me in the direction of something to look at or try I'd be grateful.

Now for the description of the code itself, which is a bit involved,
despite my having condensed it down to its bare essentials. If you're
prepared to help me make sense of this then
http://www.lawshouse.org/perl/Problem.jpg shows the thing
diagrammatically, with arrows drawn on. You can just about read the code
in the image too.

There are three modules: NFBT::ServerLib, NFBT::Utilities::Common and
NFBT::Utilities::Server. There is some requirement in them for
subroutines out of one or more of the others.

A small test program "trynfbt.pl" includes "shadow_conv" from
NFBT::Utilities::Server. The shadow_conv subroutine in
Utilities::Server imports a subroutine from ServerLib, which in turn
imports ":all: from Utilities::Common as well as the same shadow_conv
sub from Utilities::Server. (I hope you're following this). The last
piece of the jigsaw is that Utilities::Common imports XML::Twig::XPath.

Running the test program trynfbt.pl gives (These line numbers may not be
right because I knocked out blank lines and CR's to make the code
smaller to post)

"shadow_conv" is not exported by the NFBT::Utilities::Server module
Can't continue after import errors at
/usr/lib/perl5/site_perl/5.8.6/NFBT/ServerLib.pm line 28
BEGIN failed--compilation aborted at
/usr/lib/perl5/site_perl/5.8.6/NFBT/ServerLib.pm line 28.
Compilation failed in require at
/usr/lib/perl5/site_perl/5.8.6/NFBT/Utilities/Server.pm line 22.
... and three other lines that tell us no more.

But shadow_conv *is* exported, unless my brain is addled.
And removing any of the following lines makes the code run:

The import of Utilities::Common(':all')
The import of XML::Twig::XPath
.. and, of course, the other "use" statements.

Replacing XML::Twig::XPath with something else - I tried XML::Simple and
even File::Basename - also makes the code run clean; it has to be that
XML module despite the fact that it's never used. Eh?

Lastly here is the actual code from the three modules and the test
program. You might find the graphic easier ...

Test Program
============
#!/usr/bin/perl

use strict;
use warnings;

use NFBT::Utilities::Server qw ( shadow_conv );

print "This is the test routine $0\n";


The three modules
=================
------------------------------------------------------
package NFBT::ServerLib;

use 5.008;
use strict;
use warnings;

require Exporter;
our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'all' => [ qw(
write_xml_twig
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'}}, qw(
write_xml_twig
) );

print "This is NFBT::ServerLib\n";


sub write_xml_twig {
# The original write_xml_twig required several subroutines from
# Utilities::Common and also 'shadow_conv'
# Comment either of these out, problem disappears
use NFBT::Utilities::Common ":all";
use NFBT::Utilities::Server qw(shadow_conv); # "not exported"
error here
print "This is write_xml_twig in package NFBT::ServerLib\n";
}

1;

--------------------------------------------------
package NFBT::Utilities::Common;

use 5.008;
use strict;
use warnings;

require Exporter;
our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'all' => [ qw(
find_bkfile_by_id
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'}}, qw(
find_bkfile_by_id
) );

sub find_bkfile_by_id {
print "This is subroutine find_bkfile_by_id in package
NFBT::Utilities::Common\n";
use XML::Twig::XPath; # Comment this out, problem disappears. It
has to be
# XML::Twig::XPath, apparently. XML::Simple and File::Basename cause
the problem to
# disappear.
}

1;

------------------------------------------------------
package NFBT::Utilities::Server;

use 5.008;
use strict;
use warnings;

require Exporter;
our @ISA = qw(Exporter);

our %EXPORT_TAGS = ( 'all' => [ qw(
shadow_conv
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'}}, qw(
shadow_conv
) );

sub shadow_conv {
# The original shadow_conv used write_xml_twig
use NFBT::ServerLib qw(write_xml_twig); # Comment this out, problem
disappears
print "This is subroutine shadow_conv in package
NFBT::Utilities::Server\n";
}

1;
 
M

Mark Clements

Henry said:
I have a bizarre problem with packages and I'm hoping that someone can
help me find out what I'm doing wrong because I'm utterly stumped.

The error is "not exported" for something that quite clearly is exported
(details follow). The error disappears when one of several particular
There are three modules: NFBT::ServerLib, NFBT::Utilities::Common and
NFBT::Utilities::Server. There is some requirement in them for
subroutines out of one or more of the others.
You appear to have circular dependencies between these modules, and I'd
guess that the mutual importing is confusing the import/export mechanism.

There is a brief discussion at

http://www.perlmonks.org/?node_id=473692

I'd try breaking the circular dependencies for a start.


package NFBT::ServerLib;
sub write_xml_twig {
# The original write_xml_twig required several subroutines from
# Utilities::Common and also 'shadow_conv'
# Comment either of these out, problem disappears
use NFBT::Utilities::Common ":all";
use NFBT::Utilities::Server qw(shadow_conv); # "not exported" error
here
print "This is write_xml_twig in package NFBT::ServerLib\n";
}

This is an unrelated issue, but "use"ing happens at compile-time.
Putting the use statement inside the subroutine does not limit its scope
or control when it is executed. perldoc -f use has the gory details.

Mark
 
A

attn.steven.kuo

(snipped)
Now for the description of the code itself, which is a bit involved,
despite my having condensed it down to its bare essentials. If you're
prepared to help me make sense of this thenhttp://www.lawshouse.org/perl/Problem.jpgshows the thing
diagrammatically, with arrows drawn on. You can just about read the code
in the image too.

There are three modules: NFBT::ServerLib, NFBT::Utilities::Common and
NFBT::Utilities::Server. There is some requirement in them for
subroutines out of one or more of the others.

(snipped)

First of all, you have a couple of modules that mutually use
each other, namely:

NFBT::ServerLib and
NFBT::Utilities::Server

this is generally a bad idea and you should consider refactoring;
see the discussion here:

http://www.perl.com/pub/a/2000/07/p5pdigest/THISWEEK-20000709.html#Mutual_use

Remember that use statements are collected into the BEGIN block (even
if
you embed them inside a subroutine) to be evaluated at compile-time
-- see 'perldoc perlmod'.

So, I'd suggest that you refactor. A quick "work-around" would be
that
in NFBT::Utilities::Server, you change

use NFBT::ServerLib

to

require NFBT::ServerLib;
import ServerLib (qw/write_xml_twig/);

in which case:

#!/usr/bin/perl
use strict;
use warnings;
use NFBT::Utilities::Server qw ( shadow_conv );

print "This is the test routine $0\n";
shadow_conv();

ought to run as expected (because 'require', unlike 'use'
is evaluated at run-time).
 
H

Henry Law

First of all, you have a couple of modules that mutually use
each other, namely:

NFBT::ServerLib and
NFBT::Utilities::Server

this is generally a bad idea and you should consider refactoring;
see the discussion here:

http://www.perl.com/pub/a/2000/07/p5pdigest/THISWEEK-20000709.html#Mutual_use

Steven, and Mark Clements in another post, thank you very much. Even
the little I knew about how Perl programs actually hurl themselves into
action should have pointed me to the fact that this kind of circular
"use"-ing isn't a good idea. I'll have a go at some refactoring; if
that doesn't work (sounds like intellectual heavy lifting) then I might
resort to using BEGIN blocks or "require".

But one more thing: can you think of a reason why this problem has
suddenly hit? I'm doing a test installation (having written a configure
program to produce a Makefile) on a fresh machine, running FC6 (Perl
5.8.8 but I don't have a perl -v handy), but the code is built on my
development machine, FC5, also Perl 5.8.8, on which it runs perfectly,
circular references notwithstanding. But I'll bash on with the
refactoring anyway.
 
B

Ben Morrow

Quoth Mark Clements said:
You appear to have circular dependencies between these modules, and I'd
guess that the mutual importing is confusing the import/export mechanism.

This *shouldn't* be a problem, providing Perl knows the exports early
enough. Put the 'require Exporter; @ISA=...; @EXPORT=...;' stuff in a
BEGIN block, *before* you use any modules that might recursively use
this one.
This is an unrelated issue, but "use"ing happens at compile-time.
Putting the use statement inside the subroutine does not limit its scope
or control when it is executed.

....unless it's a lexically-scoped pragma like strict or warnings. The
use still happens at compile time, but some of the effects of that are
restricted to the lexical scope currently being compiled. This obviously
doesn't apply to simply importing subs, which is an operation with
global effect.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top