¿Cómo puedo incluir dinámicamente módulos Perl sin usar eval?


Necesito incluir dinámicamente un módulo Perl, pero si es posible me gustaría mantenerme alejado de eval debido a los estándares de codificación de trabajo. Esto funciona:

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

Pero necesito una manera de hacerlo sin eval si es posible. Todas las búsquedas de Google conducen al método eval, pero ninguna de otra manera.

Es posible hacerlo sin eval?

Author: brian d foy, 2009-12-16

6 answers

Uso require para cargar módulos en tiempo de ejecución. A menudo es una buena idea envolver esto en un bloque (no cadena) eval en caso de que el módulo no se pueda cargar.

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

La razón para la sintaxis eval {...} or do {...} y hacer una copia de $@ es porque $@ es una variable global que puede ser establecida por muchas cosas diferentes. Usted quiere agarrar el valor tan atómicamente como sea posible para evitar una condición de carrera donde algo más lo ha establecido a un valor diferente.

Si no conoce el nombre del módulo hasta el tiempo de ejecución tendrás que hacer la traducción entre el nombre del módulo (My::Module) y el nombre del archivo (My/Module.pm) manualmente:

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};
 44
Author: Michael Carman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-16 20:58:26

Qué tal usar el módulo core Módulo:: Load

Con su ejemplo:

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

"Module:: Load-runtime require de módulos y archivos"

"load elimina la necesidad de saber si está tratando de requerir un archivo o un módulo."

Si falla morirá con algo parecido a " No se puede localizar xxx en @INC (@INC contiene: ...".

 16
Author: DavidG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-01 14:46:52

Bueno, siempre hayrequire como en

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

Tenga en cuenta que pierde cualquier efecto que pueda haber obtenido al llamar a import en tiempo de compilación en lugar de en tiempo de ejecución.

Editar: Las compensaciones entre esto y la forma de eval son: eval le permite usar la sintaxis normal del módulo y le da un error más explícito si el nombre del módulo no es válido (en lugar de simplemente no encontrado). OTOH, la forma de evaluación es (potencialmente) más sujeta a la inyección arbitraria de código.

 10
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-16 20:15:35

No, no es posible sin eval, ya que require() necesita el nombre del módulo bareword, como se describe en perldoc-f require. Sin embargo, no es un mal uso de eval, ya que no permite la inyección de código arbitrario (suponiendo que tenga control sobre el contenido del archivo que está requireing, por supuesto).

EDITAR: Código modificado a continuación, pero estoy dejando la primera versión para completar.

yo uso yo solía usar este poco de azúcar módulo para realizar cargas dinámicas en tiempo de ejecución:

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's 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: $@" };
}

1;

PS. Estoy mirando esta definición (la escribí hace bastante tiempo) y estoy pensando en agregar esto: $class->export_to_level(1, undef, @imports);... debe trabajar, pero no se prueba.

EDITAR: versión 2 ahora, mucho mejor sin una evaluación (gracias ysth)::)

package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's 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;
 4
Author: Ether,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-16 21:20:10

Class:: MOP en CPAN tiene un método load_class para esto: http://metacpan.org/pod/Class::MOP

 1
Author: Terrence Brannon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-05 20:55:13

Me gusta hacer cosas como..

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

 0
Author: ShoeLace,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-06 23:31:59