¿Cómo puedo evitar una llamada de 'morir' en una biblioteca de Perl que no puedo modificar?

StackOverflow https://stackoverflow.com/questions/451227

  •  19-08-2019
  •  | 
  •  

Pregunta

Sí, el problema es con una biblioteca que estoy usando, y no, no puedo modificarla. Necesito una solución alternativa.

Básicamente, estoy tratando con una biblioteca Perl mal escrita, que sale con 'morir' cuando se encuentra una cierta condición de error al leer un archivo. Llamo a esta rutina desde un programa que está recorriendo miles de archivos, algunos de los cuales son malos. Los archivos malos suceden; Solo quiero que mi rutina registre un error y continúe.

SI PODRÍA modificar la biblioteca, simplemente cambiaría el

die "error";

a un

print "error";return;

, pero no puedo. ¿Hay alguna forma de adaptar la rutina para que los archivos defectuosos no bloqueen todo el proceso?

PREGUNTA DE SEGUIMIENTO: Usando un " eval " atender la llamada propensa a fallas funciona bien, pero ¿cómo configuro el manejo de errores detectables dentro de ese marco? Para describir:

Tengo una subrutina que llama a la biblioteca, que se bloquea, a veces muchas veces. En lugar de colocar cada llamada dentro de esta subrutina con una evaluación {}, simplemente dejo que muera, y uso una evaluación {} 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 atrapar en la función (). ¿Cuál es la forma más adecuada / elegante de diseñar la captura de errores en la subrutina y la rutina de llamada para que obtenga el comportamiento correcto para los errores detectados y no detectados?

¿Fue útil?

Solución

Podría envolverlo en un eval . Ver:

perldoc -f eval

Por ejemplo, podría 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 llama a die , $ @ contiene la cadena que se le pasó.

Otros consejos

¿Atrapa $ SIG {__ DIE__} ? Si lo hace, entonces es más local que tú. Pero hay un par de estrategias:

  • Puede evocar su paquete y anular morir:

    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 atraparlo . (busque $ SIG en la página, Markdown no está manejando 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 llame y usarlo para cargar su controlador $ SIG anulando que .

    my $dumb_package_do_something_dumb = \&Dumb::do_something_dumb;
    *Dumb::do_something_dumb = sub { 
        $SIG{__DIE__} = ...
        goto &$dumb_package_do_something_dumb;
    };
    
  • O anular una función incorporada 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 anulará die a nivel mundial, la única forma de recuperar die es abordarlo como CORE :: die .

Alguna combinación de esto funcionará.

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 cambia la fuente original en absoluto.

Primero, cargue el paquete original para obtener todas las definiciones originales. Una vez que el original esté en su lugar, puede redefinir la problemática subrutina:

 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 si desea 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 controlas ese directorio, puedes editar los archivos que contiene. 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 aunque alguien actualice 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 esa clase 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top