Как я могу подключиться к Perl use/require, чтобы я мог генерировать исключение?

Если файл уже загружен, можно ли как-то подключиться к use/require, чтобы я мог создать исключение? В моем предстоящем nextgen::blacklist я пытаюсь умереть если используются определенные модули. Я использую метод объектного хука, как упоминалось в perldoc -f require: есть три подобных хука: объект, массив с подссылкой и подссылка. Примером в этом посте является хук объекта, вы можете найти мою попытку хука sub-ref в nextgen::blacklist.

Синтаксис, который я хочу, выглядит примерно так:

perl -Mnextgen -E"use NEXT"

package Foo;
use nextgen;
use NEXT;

В идеале должно выдаваться такое сообщение:

nextgen::blacklist violation with import attempt for: [ NEXT (NEXT.pm) ] try 'use mro' instead.

Я пробовал это кучей разных способов.

package Class;
use Data::Dumper;
use strict;
use warnings;

sub install {
  unshift @main::INC, bless {}, __PACKAGE__
    unless ref $main::INC[0] eq __PACKAGE__
  ;
}

sub reset_cache { undef %main::INC }

sub Class::INC {
  my ( $self, $pmfile ) = @_;
  warn Dumper [\%main::INC, $pmfile];
  #undef %INC;
} 

package main;
  BEGIN { Class->install; undef %main::INC }
  use strict;
  use strict;
  use strict;
  use strict;
  use warnings;
  use strict;
  use warnings;

Кажется, что %INC устанавливается только после этих хуков. Меня интересует все, что позволит мне создать исключение. Если предпринята попытка загрузить/перезагрузить модуль, несмотря на его статус зависимости от других модулей, которые не используют мою прагму, я хочу умереть.

package Foo;
use NEXT;

package main;
use Foo; (which uses Next.pm);
use NEXT.pm; ## Throw exception

person Evan Carroll    schedule 06.10.2010    source источник


Ответы (1)


Вероятно, вы захотите поместить кодовую ссылку в начало @INC, как описано в perldoc -f require. Оттуда вы можете вызвать исключения, чтобы предотвратить загрузку определенных модулей, или ничего не делать, чтобы позволить require продолжить свою обычную работу по поиску модуля в других записях @INC.

$ perl -E'BEGIN { unshift @INC, sub { die q{no NEXT} if pop eq q{NEXT.pm}; () }; }; use Carp; say q{success}'
success
$ perl -E'BEGIN { unshift @INC, sub { die q{no NEXT} if pop eq q{NEXT.pm}; () }; }; use NEXT; say q{success}'
no NEXT at -e line 1.
BEGIN failed--compilation aborted at -e line 1.

Если вы хотите, чтобы это поведение было лексическим, вы должны использовать хеш подсказок Perl %^H. Работать с этим немного неудобно, поэтому я рекомендую использовать Devel::Pragma, который может позаботиться обо всех кровавых деталях для вас.

Как вы указали, хуки @INC не будут выполняться для уже загруженного модуля. Если вам также нужно подключиться к use или require загруженного модуля, переопределение CORE::GLOBAL::require будет работать, так как оно вызывается при каждой попытке загрузить модуль.

$ perl -E'BEGIN { *CORE::GLOBAL::require = sub { warn @_ } } use NEXT; use NEXT;'
NEXT.pm at -e line 1
NEXT.pm at -e line 1.

Кроме того, как сопровождающий NEXT, я полностью одобряю запрет на его использование людьми вообще и когда-либо. :-)

person rafl    schedule 06.10.2010
comment
Спасибо за слова поддержки, я попытался добавить кодовую ссылку в @INC, проверьте nextgen::blacklist для этой попытки. Есть три хука, которые обеспечивают эту функциональность (массив с кодовой ссылкой, объект и кодовую ссылку), также в perldoc, на который вы ссылаетесь. - person Evan Carroll; 06.10.2010
comment
Так в чем проблема? Я только что добавил крошечный пример этого в свой ответ, и он работает так, как и ожидалось. - person rafl; 06.10.2010
comment
Согласно заголовку (и тексту), если NEXT включено в зависимость, я все равно хочу, чтобы use NEXT выдавало исключение. Это означает, что если вы используете Foo.pm, который зависит от NEXT. Я все еще не хочу, чтобы NEXT разрешалось. Но я не хочу, чтобы включение для Foo.pm вызывало исключение. - person Evan Carroll; 06.10.2010
comment
Да. Такой способ может сработать, Хисант только что обратил меня на свой подход, вдохновленный ACME::Magic::Poney - person Evan Carroll; 06.10.2010
comment
Очень хорошо сделано. Спасибо за помощь, я загрузил nextgen::blacklist, и сейчас он находится на пути к CPAN. - person Evan Carroll; 07.10.2010