Come posso aggirare una chiamata "die" in una libreria Perl che non posso modificare?

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

  •  19-08-2019
  •  | 
  •  

Domanda

Sì, il problema è con una libreria che sto usando e no, non posso modificarlo. Ho bisogno di una soluzione alternativa.

Fondamentalmente, ho a che fare con una libreria Perl scritta male, che esce con 'die' quando si verifica una certa condizione di errore durante la lettura di un file. Chiamo questa routine da un programma che sta eseguendo il ciclo tra migliaia di file, alcuni dei quali sono cattivi. Si verificano file danneggiati; Voglio solo che la mia routine registri un errore e vada avanti.

Se potessi modificare la libreria, cambierei semplicemente

die "error";

in a

print "error";return;

, ma non posso. Esiste un modo in cui posso elaborare la routine in modo che i file danneggiati non si blocchino l'intero processo?

DOMANDA SEGUENTE: Utilizzo di una "quotazione" " gestire la chiamata soggetta a crash funziona bene, ma come posso impostare la gestione per errori intercettabili in quel framework? Per descrivere:

Ho una subroutine che chiama la libreria che si arresta in modo anomalo a volte molte volte. Invece di aggiungere a ogni chiamata all'interno di questa subroutine un eval {}, lascio solo che muoia e uso un eval {} al livello che chiama il mio subroutine:

my $status=eval{function($param);};
unless($status){print $@; next;}; # print error and go to next file if function() fails

Tuttavia, ci sono condizioni di errore che posso e faccio catturare in function (). Qual è il modo più corretto / elegante per progettare la rilevazione degli errori nella subroutine e nella routine di chiamata in modo da ottenere il comportamento corretto sia per gli errori catturati che per quelli non rilevati?

È stato utile?

Soluzione

Potresti avvolgerlo in un eval . Vedi:

perldoc -f eval

Ad esempio, potresti scrivere:

# warn if routine calls die
eval { routine_might_die }; warn $@ if $@;

Questo trasformerà l'errore fatale in un avvertimento, che è più o meno quello che hai suggerito. Se viene chiamato die , $ @ contiene la stringa passata ad essa.

Altri suggerimenti

Intrappola $ SIG {__ DIE__} ? Se lo fa, allora è più locale di te. Ma ci sono un paio di strategie:

  • Puoi evocare il suo pacchetto e override die:

    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 @_;
       }
    } 
    
  • In caso contrario, può intercettare . (cerca $ SIG nella pagina, markdown non gestisce il link 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;
    
  • Potrebbe essere necessario scansionare la libreria, trovare un sottotitolo che sempre e utilizzarlo per caricare il gestore $ SIG sovrascrivendo che .

    my $dumb_package_do_something_dumb = \&Dumb::do_something_dumb;
    *Dumb::do_something_dumb = sub { 
        $SIG{__DIE__} = ...
        goto &$dumb_package_do_something_dumb;
    };
    
  • O sovrascrive un builtin che sempre chiama ...

    package Dumb; 
    use subs 'chdir';
    sub chdir { 
        $SIG{__DIE__} = ...
        CORE::chdir @_;
    };
    
  • Se tutto il resto fallisce, puoi frustare gli occhi del cavallo con questo:

    package CORE::GLOBAL;
    use subs 'die';
    
    sub die { 
        ... 
        CORE::die @_;
    }
    

Questo sovrascriverà die a livello globale, l'unico modo per poter tornare die è indirizzarlo come CORE :: die .

Alcune combinazioni funzioneranno.

Sebbene la modifica di un die per non morire abbia una soluzione specifica, come mostrato nelle altre risposte, in generale è sempre possibile ignorare le subroutine in altri pacchetti. Non cambi affatto la fonte originale.

Innanzitutto, carica il pacchetto originale in modo da ottenere tutte le definizioni originali. Una volta che l'originale è a posto, puoi ridefinire la problematica subroutine:

 BEGIN {
      use Original::Lib;

      no warnings 'redefine';

      sub Original::Lib::some_sub { ... }
      }

Puoi anche tagliare e incollare la definizione originale e modificare ciò di cui hai bisogno. Non è un'ottima soluzione, ma se non puoi cambiare l'origine originale (o vuoi provare qualcosa prima di cambiare l'originale), può funzionare.

Oltre a ciò, puoi copiare il file sorgente originale in una directory separata per la tua applicazione. Dal momento che controlli quella directory, puoi modificare i file in essa contenuti. Modifichi quella copia e la carichi aggiungendo quella directory al percorso di ricerca del modulo Perl:

use lib qw(/that/new/directory);
use Original::Lib;  # should find the one in /that/new/directory

La tua copia rimane attiva anche se qualcuno aggiorna il modulo originale (anche se potresti dover unire le modifiche).

Ne parlo un po 'in Mastering Perl , dove mostro alcune altre tecniche da fare questo genere di cose. Il trucco è non rompere le cose ancora di più. Il modo in cui non rompi le cose dipende da cosa stai facendo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top