1

So, I'm trying to import a module into a Perl package and I try to make it in the cleanest way possible. First, I read some of the previous threads (like: link).

The module is located in the following path: /p/disk/tools/perl/5.26.1/lib64/site_perl. The module is Data::TreeDumper.

I used the FindBin module in order to import it:

BEGIN {
    use Exporter ();
    use vars qw(@ISA @EXPORT_OK  %EXPORT_TAGS);
    use FindBin;
    use lib "$FindBin::RealBin";
    use lib "$FindBin::Bin";
    use lib "$FindBin::Bin/../lib";
    use lib "/p/disk/tools/perl/5.26.1/lib64/site_perl";
    @ISA       = qw(Exporter);
    @EXPORT_OK = qw(# ALL subs
                   );
    %EXPORT_TAGS = (all => [@EXPORT_OK]);
}
use constants qw(:all);
use Data::TreeDumper;

It works for me, but the path is hardcoded. I would like to include the path into the constants package (my package). But it kind of a circle because in order to use the constants (and the path) I need to import it first, but then, from where? huh.

So I thought to remove the use lib "/p/disk/tools/perl/5.26.1/lib64/site_perl"; line and somehow import first the constants file and then do something like:

use constants qw(:all);
use lib $PATH_TO_THE_MODULE; # represents path to the module area
use Data::TreeDumper;

But It does not work for. Is it possible to achieve it?

Before trying to import the module (didn't know it was installed in a global place), I installed it locally and it worked. But I prefer not to keep the non-mine modules in the same directory as the project. So IT guys installed the module in a global area and I just asking if it is possible to import it in a clean way.

EDIT: I'm trying to insert the path /p/disk/tools/perl/5.26.1/lib64/site_perl into the constants file in a global variable (let's call it $PATH_TO_THE_MODULE). Then I want to import from the other package the constants package so I use FindBin. And Then I want to import the module using the $PATH_TO_THE_MODULE variable which points to the area of the module. So it should look something like this:

use constants qw(:all);
use lib $PATH_TO_THE_MODULE; # represents a path to the module area
use Data::TreeDumper;

But it does not work. I tried to switch to require because I understood that require will use dynamically. But it also fails with Can't locate Data/TreeDumper.pm in @INC.

So if we connect both pieces of code we get:

package test;
$|=1;
use strict;
use warnings;
BEGIN {
    use Exporter ();
    use vars qw(@ISA @EXPORT_OK  %EXPORT_TAGS);
    use FindBin;
    use lib "$FindBin::RealBin";
    use lib "$FindBin::Bin";
    use lib "$FindBin::Bin/../lib";
    @ISA       = qw(Exporter);
    @EXPORT_OK = qw(# ALL subs
                   );
    %EXPORT_TAGS = (all => [@EXPORT_OK]);
}
use constants qw(:all);
use lib $PATH_TO_THE_MODULE; # represents a path to the module area
use Data::TreeDumper;
print "Hi\n";

constants package:

package constants;
$|=1;
use strict;
use warnings;
BEGIN {
    use Exporter ();
    use vars qw(@ISA @EXPORT_OK  %EXPORT_TAGS);
    use FindBin;
    use lib "$FindBin::RealBin";
    use lib "$FindBin::Bin";
    use lib "$FindBin::Bin/../lib";
    @ISA       = qw(Exporter);
    @EXPORT_OK = qw($PATH_TO_THE_MODULE);
    %EXPORT_TAGS = (all => [@EXPORT_OK]);
}
our $PATH_TO_THE_MODULE = "/p/disk/tools/perl/5.26.1/lib64/site_perl";

If I'll remove use lib $PATH_TO_THE_MODULE; and use Data::TreeDumper; from the test package and I'll print $PATH_TO_THE_MODULE, it will compile and print the path as expected.

TTaJTa4
  • 810
  • 1
  • 8
  • 22
  • Dynamically importing stuff is done by `require`. Consider reading the [documentation of use](http://perldoc.perl.org/functions/use.html), which tells you the exact incantation you need for dynamically loading a module. – Corion Feb 01 '19 at 18:26
  • 2
    If you have modules in a position related to the script, use `use lib $FindBin::RealBin/...";`. But if you have modules installed elsewhere, set env var `PERL5LIB` to its location – ikegami Feb 01 '19 at 19:11
  • @ikegami yes but I want both. First to import contants package which relative (using `FindBin`) and then use a const (which is a path) to import the area from it. – TTaJTa4 Feb 01 '19 at 19:25
  • @TTaJTa4 If I well understood your question, you need to do some dynamic loading of modules at runtime.. I'm right? If yes, see https://perldoc.perl.org/Module/Load.html core module. You can even use it in BEGIN blocks. – Hannibal Feb 01 '19 at 20:17
  • 1
    @Hannibal, Nope, they want to load a module (Data::TreeDumper) at compile-time. The location of that module is to be provided by a variable exported by their own module (`constants.pm`). There's a bug in their module (`constants.pm`) with which the OP needs help, but they are refusing to show it. Therefore, the question is off-topic as it stands. – ikegami Feb 01 '19 at 20:37
  • 1
    The posted code works. While having a non-fatal bug that results in a warning, while changing `@INC` in a module (which doesn't make any sense), and while being extremely complex (especially for someone search for simplicity), the posted code does add `/p/disk/tools/perl/5.26.1/lib64/site_perl` to `@INC`. If you still get the error you claim you get, it's because the module isn't where you claim it is, or there's some issue (e.g. permissions) that prevents it from being accessed. Cheers. You sure made it hard on yourself and everyone else, but you finally got there. – ikegami Feb 02 '19 at 18:25
  • 2
    @ikegami OMG. You clearly miss the issue and you behave like you know it all. `@INC` does not points to the `$PATH_TO_THE_MODULE` because it fails in compile time and `$PATH_TO_THE_MODULE` is know only in run time. In order the compiler to read `$PATH_TO_THE_MODULE` he needs to know it in RUNTIME. That is my question, how can I get the compiler to know about that variable in compile time. – TTaJTa4 Feb 02 '19 at 21:14
  • It is not a bug thread, rather its a question on how can I make the compiler know the variable name in run-time so it could use `use $path;`. It does not work because `$path` is known only in run-time (even if it is a const). Why are you all miss it? – TTaJTa4 Feb 02 '19 at 21:32
  • 2
    Re "*because it fails in compile time and $PATH_TO_THE_MODULE is know only in run time.*", You are mistaken. The code you posted compiles just fine, and `$PATH_TO_THE_MODULE` is both created and initialized while `test.pm` is being compiled and before `use lib $PATH_TO_THE_MODULE;` is compiled and executed. – ikegami Feb 02 '19 at 21:56
  • 2
    Re "*is known only in run-time (even if it is a const)*", 1) It's not a const, 2) There's no such thing as a run-time const 3) It's known an compile-time 4) While it's initialised during constants.pm's runtime, that happens during test.pm's compile time. Specifically, before `use lib $PATH_TO_THE_MODULE;` is compiled and executed. – ikegami Feb 02 '19 at 22:02

1 Answers1

0

You claim your code doesn't work, and it's because the initialization of $PATH_TO_THE_MODULE happens at run-time.

Well, your code does work, and it's because there isn't a single compile phase and a single run phase.

Let's say you had the following script:

use constants ':all';
use lib $PATH_TO_THE_MODULE;
use Data::TreeDumper qw();
print "Hi\n";

And let's simplify your module to the following:

package constants;
use Exporter qw( import );
our @EXPORT_OK = qw( $PATH_TO_THE_MODULE );
our %EXPORT_TAGS = ( all => \@EXPORT_OK );
our $PATH_TO_THE_MODULE = "/path/to/site_perl";

Keeping in mind that use Foo; is roughly equivalent to BEGIN { use Foo; import Foo; } and that BEGIN blocks are executed as soon as they are compiled, the following describes what happens when you execute the script:

  1. Compile script.pl
    1. Compile use constants ':all';
    2. Execute require constants;
      1. Compile constants.pm
        1. Compile package constants;
        2. Compile use Exporter qw( import );
          1. Execute require Exporter;
            1. Compile Exporter.pm [...]
            2. Execute Exporter.pm [...]
          2. Execute import Exporter qw( import );
        3. Compile our @EXPORT_OK = qw( $PATH_TO_THE_MODULE );
        4. Compile our %EXPORT_TAGS = ( all => \@EXPORT_OK );
        5. Compile our $PATH_TO_THE_MODULE = "/path/to/site_perl";
      2. Execute constants.pm
        1. Execute our @EXPORT_OK = qw( $PATH_TO_THE_MODULE );
        2. Execute our %EXPORT_TAGS = ( all => \@EXPORT_OK );
        3. Execute our $PATH_TO_THE_MODULE = "/path/to/site_perl";
    3. Execute import constants;
    4. Compile use lib $PATH_TO_THE_MODULE;
    5. Execute require lib;
      1. Compile lib.pm [...]
      2. Execute lib.pm [...]
    6. Execute import lib $PATH_TO_MODULE;
    7. Compile use Data::TreeDumper qw();
    8. Execute require Data::TreeDumper;
      1. Compile Data/TreeDumper.pm [...]
      2. Execute Data/TreeDumper.pm [...]
    9. Compile print "Hi\n";
  2. Execute script.pl
    1. Execute print "Hi\n";

From the above, we see the following happening:

  1. [1.2.1.5] $constants::PATH_TO_THE_MODULE is created
  2. [1.2.2.3] $constants::PATH_TO_THE_MODULE is set to the path
  3. [1.3] $main::PATH_TO_THE_MODULE is created as an alias to $constants::PATH_TO_THE_MODULE
  4. [1.6] $main::PATH_TO_THE_MODULE is added to @INC
  5. [1.8] Data::TreeDumper is loaded

It's ironic that you claim that only one of the following snippets work and the other one doesn't when they are fundamentally the same:

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

use constants qw( $PATH_TO_THE_MODULE );
use lib $PATH_TO_THE_MODULE;
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • *`import Foo;`* - or in a slightly less misleading syntax, `Foo->import();`. – melpomene Feb 02 '19 at 23:13
  • @melpomene, That's actually more misleading. `import` is an operator, not a method name. – ikegami Feb 02 '19 at 23:15
  • 1
    Since when? `import` is absolutely a method name and called as a method (which is why inheriting from `Exporter` gives you access to its `import` method). – melpomene Feb 02 '19 at 23:15
  • Let me rephrase. It's documented as an operator, and it doesn't behave like a method call (it not an error if the method doesn't exist). Seeing as it's different from a normal method call, it's good to make it look different. – ikegami Feb 02 '19 at 23:19
  • It is [documented as a method call](https://perldoc.pl/functions/use): "*[`use Module LIST`] is exactly equivalent to `BEGIN { require Module; Module->import( LIST ); }`*" – melpomene Feb 02 '19 at 23:20
  • I just checked; both `perldoc -f use` and `perldoc -f import` refer to `import` as a normal (user-defined) subroutine / method. Where is it documented as an operator? – melpomene Feb 02 '19 at 23:23
  • "perldoc -f" only lists operators. It returns information from perlfunc, which is the docs for named operators. – ikegami Feb 03 '19 at 00:06
  • It also lists a few other things, such as `tr`, `DESTROY`, `__SUB__`, `CORE`, `__DATA__` or `import`. – melpomene Feb 03 '19 at 02:01
  • All of which are special. – ikegami Feb 03 '19 at 02:03
  • Except it's wrong. Like I already said, `import` doesn't behave like an ordinary method. Pretending it's an ordinary method is therefore a bad thing. – ikegami Feb 03 '19 at 02:22
  • 1
    The only thing special about it is that you can call it without an error even if it doesn't exist (I believe there's a special case for method lookup in the interpreter?). But that happens regardless of the calling syntax. Besides, indirect object syntax can be used with other methods as well: `foo bar $baz` means `bar->foo($baz)`, and `import Foo LIST` means `Foo->import(LIST)`. It's just that the latter syntax is less ambiguous. – melpomene Feb 03 '19 at 11:13
  • But you don't use it for other method calls, so that's irrelevant – ikegami Feb 03 '19 at 16:43