veterano - ¿Cómo puedo evitar una llamada de ''muerte'' en una biblioteca de Perl que no puedo modificar?
veterano lenguaje de programacion (3)
¿ $SIG{__DIE__}
? Si lo hace, entonces es más local que tú. Pero hay un par de estrategias:
Puede evocar su paquete y override dado:
package Library::Dumb::Dyer; use subs ''die''; sub die { my ( $package, $file, $line ) = caller(); unless ( $decider->decide( $file, $package, $line ) eq ''DUMB'' ) { say "It''s a good death."; die @_; } }
Si no, puede trap . (busque $ SIG en la página, el descuento no maneja el enlace completo).
my $old_die_handler = $SIG{__DIE__}; sub _death_handler { my ( $package, $file, $line ) = caller(); unless ( $decider->decide( $file, $package, $line ) eq ''DUMB DIE'' ) { say "It''s a good death."; goto &$old_die_handler; } } $SIG{__DIE__} = /&_death_handler;
Puede que tenga que escanear la biblioteca, encontrar un sub que siempre llama, y usar eso para cargar su controlador
$SIG
anulandothat
.my $dumb_package_do_something_dumb = /&Dumb::do_something_dumb; *Dumb::do_something_dumb = sub { $SIG{__DIE__} = ... goto &$dumb_package_do_something_dumb; };
O anula un built-in que siempre llama ...
package Dumb; use subs ''chdir''; sub chdir { $SIG{__DIE__} = ... CORE::chdir @_; };
Si todo lo demás falla, puedes azotar los ojos del caballo con esto:
package CORE::GLOBAL; use subs ''die''; sub die { ... CORE::die @_; }
Esto se anulará en todo el mundo, la única forma en que puedes volver a die
es abordarlo como CORE::die
.
Alguna combinación de esto funcionará.
Sí, el problema es con una biblioteca que estoy usando, y no, no puedo modificarlo. Necesito una solución.
Básicamente, estoy tratando con una biblioteca Perl mal escrita, que sale con ''morir'' cuando se encuentra una cierta condición de error leyendo un archivo. Llamo a esta rutina de un programa que recorre miles de archivos, algunos de los cuales son malos. Archivos malos suceden; Solo quiero que mi rutina registre un error y continúe.
SI PODRÍA modificar la biblioteca, simplemente cambiaría la
die "error";
a un
print "error";return;
, pero no puedo. ¿Hay alguna forma de que pueda controlar la rutina para que los archivos defectuosos no bloqueen todo el proceso?
PREGUNTA DE SEGUIMIENTO: Usar una "evaluación" para activar la llamada propensa a bloqueos funciona bien, pero ¿cómo configuro el manejo de los errores capturables dentro de ese marco? Para describir:
Tengo una subrutina que llama a la biblioteca, que se cuelga, a veces muchas veces. En lugar de relajar cada llamada dentro de esta subrutina con una evaluación {}, simplemente permito que muera, y uso una eval {} en el nivel que llama a mi subrutina:
my $status=eval{function($param);};
unless($status){print $@; next;}; # print error and go to next file if function() fails
Sin embargo, hay condiciones de error que puedo capturar en function (). ¿Cuál es la forma más adecuada / elegante de diseñar la captura de errores en la subrutina y la rutina de llamadas para que obtenga el comportamiento correcto para los errores detectados y no detectados?
Aunque cambiar un die
para que no muera tiene una solución específica como se muestra en las otras respuestas, en general siempre puede anular las subrutinas en otros paquetes. No cambias la fuente original en absoluto.
Primero, cargue el paquete original para que obtenga todas las definiciones originales. Una vez que el original está en su lugar, puede redefinir la subrutina problemática:
BEGIN {
use Original::Lib;
no warnings ''redefine'';
sub Original::Lib::some_sub { ... }
}
Incluso puede cortar y pegar la definición original y ajustar lo que necesita. No es una gran solución, pero si no puede cambiar la fuente original (o quiere probar algo antes de cambiar el original), puede funcionar.
Además de eso, puede copiar el archivo fuente original en un directorio separado para su aplicación. Como usted controla ese directorio, puede editar los archivos en él. Usted modifica esa copia y la carga agregando ese directorio a la ruta de búsqueda del módulo de Perl:
use lib qw(/that/new/directory);
use Original::Lib; # should find the one in /that/new/directory
Su copia se mantiene, incluso si alguien actualiza el módulo original (aunque es posible que deba fusionar los cambios).
Hablo bastante sobre esto en Mastering Perl , donde muestro algunas otras técnicas para hacer ese tipo de cosas. El truco es no romper las cosas aún más. Cómo no rompes las cosas depende de lo que estás haciendo.
Podrías envolverlo en una eval
. Ver:
perldoc -f eval
Por ejemplo, podrías escribir:
# warn if routine calls die
eval { routine_might_die }; warn $@ if $@;
Esto convertirá el error fatal en una advertencia, que es más o menos lo que sugirió. Si se die
, $@
contiene la cadena que se le pasa.