Подтвердить что ты не робот

Как я могу динамически включать модули Perl без использования eval?

Мне нужно динамически включать модуль Perl, но, если возможно, хотелось бы избегать eval из-за стандартов рабочего кодирования. Это работает:

$module = "My::module";
eval("use $module;");

Но мне нужен способ сделать это без eval, если это возможно. Все поисковые запросы Google приводят к методу eval, но ни в коем случае.

Можно ли это сделать без eval?

4b9b3361

Ответ 1

Используйте require для загрузки модулей во время выполнения. Часто рекомендуется обернуть это в блок (не строку) eval, если модуль не может быть загружен.

eval {
    require My::Module;
    My::Module->import();
    1;
} or do {
   my $error = [email protected];
   # Module load failed. You could recover, try loading
   # an alternate module, die with $error...
   # whatever appropriate
};

Причина синтаксиса eval {...} or do {...} и создание копии [email protected] заключается в том, что [email protected] - это глобальная переменная, которая может быть задана многими разными способами. Вы хотите получить значение как можно более атомарным, чтобы избежать условия гонки, когда что-то еще установило его на другое значение.

Если вы не знаете имя модуля до выполнения, вам придется вручную выполнить перевод между именем модуля (My:: Module) и именем файла (My/Module.pm):

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = [email protected];
    # ...
};

Ответ 2

Как насчет использования основного модуля Модуль:: Загрузка

В вашем примере:

use Module::Load;
my $module = "My::module";
load $module;

"Модуль:: Загрузка - время выполнения как модулей, так и файлов"

"устраняет необходимость знать, пытаетесь ли вы требовать либо файл, либо модуль."

Если он терпит неудачу, он умрет с чем-то вроде "Невозможно найти xxx в @INC (@INC содержит:...".

Ответ 3

Ну, всегда require, как в

require 'My/Module.pm';
My::Module->import();

Обратите внимание, что вы теряете любые эффекты, которые вы, возможно, получили от import, вызываемых во время компиляции, а не время выполнения.

Изменить: компромиссы между этим и eval образом: eval позволяет использовать стандартный синтаксис модуля и дает более явную ошибку, если имя модуля недействительно (в отличие от просто не найденного). OTOH, метод eval (потенциально) более подвержен произвольной инъекции кода.

Ответ 4

Нет, это невозможно без eval, так как require() требуется имя модуля bareword, как описано в perldoc -f require. Однако это не злое использование eval, так как оно не позволяет вводить произвольный код (при условии, что вы, конечно, контролируете содержимое файла, который вы require ing).

EDIT: Код изменен ниже, но я оставляю первую версию для полноты.

Я использую Я использовал этот маленький модуль сахара для выполнения динамических нагрузок во время выполнения:

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;
    eval "require $class" or do { die "Ack, can't load $class: [email protected]" };
}

1;

PS. Я смотрю на это определение (я написал это довольно давно), и я размышляю над добавлением этого: $class->export_to_level(1, undef, @imports);... он должен работать, но не проверен.

EDIT: версия 2 теперь гораздо приятнее без eval (спасибо ysth)::)

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;

    (my $file = $class) =~ s|::|/|g;
    $file .= '.pm';
    require $file;  # will die if there was an error
}

1;

Ответ 6

Мне нравится делать такие вещи, как..

require Win32::Console::ANSI if ( $^O eq "MSWin32" );