11

Let's say I want to wrap require such that,

package MyModule;
use Data::Dumper;

Would output either,

MyModule -> Data::Dumper
MyModule -> Data/Dumper.pm

For all packages and all requires/use statements. How could I do it?

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
  • 1
    It would be `goto &CORE::require` - CORE::GLOBAL:: is for overriding not calling, so you just called your own override. Except that require is not one of the builtins callable from CORE::, as noted in the [CORE docs](https://perldoc.perl.org/CORE). – Grinnz Nov 30 '20 at 20:49
  • s/callable from CORE::/referenceable as a subroutine/ – Grinnz Nov 30 '20 at 20:57
  • I think the terminology you're searching for is that you want to instrument `use` and `require`. – lordadmira Nov 30 '20 at 22:17

4 Answers4

12
BEGIN {
   unshift @INC, sub {
      printf "%s -> %s\n", ( caller() )[0], $_[1];
      return;
   };
}

See the paragraph starting with "You can also insert hooks into the import facility" in require's documentation.

ikegami
  • 367,544
  • 15
  • 269
  • 518
8

You cannot reference the require builtin as a subroutine such as for goto, but you can call it from CORE.

use strict;
use warnings;
BEGIN {
  *CORE::GLOBAL::require = sub {
    printf "%s -> %s\n", [caller()]->[0], $_[0];
    CORE::require $_[0];
  }
};
use Data::Dumper;

You may also consider Devel::TraceUse which is similar to the above but more robust and informative, and can easily be invoked from the commandline.

Grinnz
  • 9,093
  • 11
  • 18
4

You can look inside Devel::TraceUse to see working code for what you are trying to do. But, changing definitions isn't a good plan because you don't know who else also changed definitions and will wonder why their stuff stops working.

You don't need to (or should) override require. Put a code reference in @INC and return false at the end so it looks like that failed and Perl will move on to the next thing in @INC:

#!perl
use v5.10;

# https://stackoverflow.com/a/2541138/2766176
BEGIN {
unshift @INC, sub {
    my( $package, $file ) = caller(0);
    say "$package -> $_[1]";
    return 0;
    };

}

use Data::Dumper;

say "Hello";

This outputs:

main -> Data/Dumper.pm
Data::Dumper -> constant.pm
constant -> strict.pm
constant -> warnings/register.pm
warnings::register -> warnings.pm
Data::Dumper -> Carp.pm
Carp -> overloading.pm
Carp -> Exporter.pm
Data::Dumper -> XSLoader.pm
Data::Dumper -> bytes.pm
Hello
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • Also, you're suppose to return nothing, not something false. – ikegami Dec 01 '20 at 05:36
  • Ah, you did beat me. That's what I get for leaving my answer in a tab. :( As for the return value, it's not a big deal , but the wording in the docs is "If an empty list, undef, or nothing that matches the first 3 values above is returned, then require looks at the remaining elements of @INC." – brian d foy Dec 01 '20 at 17:57
  • 1
    Oh. The latest docs say "The subroutine should return either nothing or else a list of up to four values in the following order:" (Link in my answer.) – ikegami Dec 01 '20 at 18:55
1

Weird requirement needs weird solution: Use a source filter.

#!/usr/bin/perl
{   package Wrap::Use;
    use Filter::Simple sub {
        warn $1 while /use (.*?);/sg;                                  # stupid SO: /
    };
}

BEGIN { Wrap::Use->import }

use strict;
use warnings;
use Time::Piece;
choroba
  • 231,213
  • 25
  • 204
  • 289