0

I have test_utils.pm:

package TestUtils;
use strict;
use warnings;

use Exporter 'import';
our @EXPORT = qw / print_diagnostic /;

sub print_diagnostic { <some_code_here> }

And I'd like to call print_diagnostic from my main script tester.pl which is located in the same folder (I've used this solution):

#!/usr/bin/perl
use lib dirname (__FILE__);
use test_utils qw (:ALL);

print_diagnostic(<some_message>);

The problem is that I'm getting

Undefined subroutine &main::print_diagnostic called at ...

Calling the function with explicit package name TestUtils::print_diagnostic works fine, but for various reasons I do want to call it w/o such a prefix.

use test_utils qw(print_diagnostic);

and

require test_utils;

yields the same results.

I've dug over few scores of similar questions, tried the answers and "solutions", but all that doesn't seem to work for me, and I'm getting really confused with all that "export", "import" and all the things around it, that I can't seize these concepts, so counterintuitive, comparing C++ and Java.

All what I want to do, is to split very complex scripts into several modules and reuse the code (now there is a lot of duplication).

Alexey
  • 1,198
  • 1
  • 15
  • 35

1 Answers1

4
use test_utils qw( :ALL );

is equivalent to

BEGIN {
   require test_utils;
   import test_utils qw( :ALL );
}

but you want

BEGIN {
   require test_utils;
   import TestUtils qw( :ALL );
}

Use the same name for the file and the package to avoid this problem.


By the way, you should replace

use File::Basename qw( dirname );
use lib dirname(__FILE__);

with

use Cwd qw( abs_path );
use File::Basename qw( dirname );
use lib dirname(abs_path(__FILE__));

or simply

use FindBin qw( $RealBin );
use lib $RealBin;

Your version will break if a symlink to the script is used.

ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    Please don't use indirect object notation. `import Foo $bar` is better written as `Foo->import($bar)`. – melpomene Oct 30 '17 at 15:44
  • @melpomene, `import` is documented as an operator, and it doesn't work like other method calls. (It doesn't fail if the method doesn't exist.) I don't see you going around telling people not to use `print STDERR ...;` – ikegami Oct 30 '17 at 16:03
  • @ikegami, thanks a lot! This clarifies all the mess in my head ) – Alexey Oct 30 '17 at 16:14
  • @ikegami The difference is that `print` is a (built-in) operator while `import` is just a subroutine: `perl -we 'sub import; import Foo'` does a normal function call, `import(Foo)`. You're right that `import` is a special method (calls to it don't fail if the method can't be found), but nevertheless it is just a user-defined method. – melpomene Oct 30 '17 at 16:20
  • @ikegami While `import` is documented in perlfunc, it says “There is no builtin import function. It is just an ordinary method”. The `import` method is always inherited from UNIVERSAL in which case this method returns immediately (from the UNIVERSAL source: “The existence of import() below is an historical accident that can't be fixed without breaking code”). It is certainly not magic, just a specially named method expected by the `use` operator. – amon Oct 30 '17 at 16:21
  • 1
    @amon, The passage you quoted ("It is just an ordinary method") is demonstrably wrong, as I already mentioned. (e.g. `perl -e'__PACKAGE__->import; __PACKAGE__->importx'`) `import` IS very magical, and this SHOULD be made obvious to the reader. Your claim that `import` is inherited from UNIVERSAL is false. (e.g. `perl -E'say for keys %UNIVERSAL::'`.) The way I see it, I'm using the `import` operator, which may call the `import` method. – ikegami Oct 30 '17 at 16:47
  • @ikegami Thanks for that clarification. It seems [the `gv_fetchmethod_*()` function treats calls to `import` and `unimport` methods specially](https://github.com/Perl/perl5/blob/v5.26.0/gv.c#L1083). Your interpretation of `import` as an operator makes sense since it has special semantics, even though there's no `import` keyword. Apparently UNIVERSAL.pm is just a dummy that should never be loaded, thus being a red herring. – amon Oct 30 '17 at 16:55
  • A note for `$RealBin`: it will not break the script if there is a symbolic link in its path. That's what `$RealBin` is for. – shawnhcorey Oct 31 '17 at 13:44