Como posso contornar uma chamada 'Die' em uma biblioteca Perl que não posso modificar?

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

  •  19-08-2019
  •  | 
  •  

Pergunta

Sim, o problema é com uma biblioteca que estou usando e não, não posso modificá -lo. Eu preciso de uma solução alternativa.

Basicamente, estou lidando com uma biblioteca Perl mal escrita, que sai com 'Die' quando uma determinada condição de erro é encontrada lendo um arquivo. Eu chamo isso de rotina de um programa que está percorrendo milhares de arquivos, um punhado dos quais é ruim. Arquivos ruins acontecem; Eu só quero que minha rotina registre um erro e siga em frente.

Se eu pudesse modificar a biblioteca, simplesmente mudaria o

die "error";

para um

print "error";return;

, mas eu não posso. Existe alguma maneira de comprar a rotina para que os arquivos ruins não travem o processo inteiro?

Pergunta de acompanhamento: Usando um "avaliar" para pagar bem a chamada propensa a acidentes, mas como configuro o manuseio para erros de captura de captura nessa estrutura? Descrever:

Eu tenho uma sub-rotina que chama a biblioteca-que-crashes-às vezes muitas vezes. Em vez de colocar cada chamada dentro desta sub -rotina com uma avaliação {}, eu apenas permito que ela morra e uso um avaliação {} no nível que chama minha sub -rotina:

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

No entanto, existem condições de erro que eu posso capturar em function (). Qual é a maneira mais adequada/elegante de projetar a compra de erros na sub-rotina e na rotina de chamada, para que eu obtenha o comportamento correto para erros capturados e não capturados?

Foi útil?

Solução

Você poderia envolvê -lo em um eval. Ver:

perldoc -f eval

Por exemplo, você pode escrever:

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

Isso transformará o erro fatal em um aviso, que é mais ou menos o que você sugeriu. Se die é chamado, $@ Contém a string passada para ela.

Outras dicas

Isso prende $SIG{__DIE__}? Se isso acontecer, é mais local do que você. Mas existem algumas estratégias:

  • Você pode evocar seu pacote e sobrepor morrer:

    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 @_;
       }
    } 
    
  • Se não, pode armadilha isto. (Procure $ sig na página, o Markdown não está lidando com o 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;
    
  • Você pode ter que escanear a biblioteca, encontrar um sub que sempre chamadas e use isso para carregar seu $SIG manipulador substituindo that.

    my $dumb_package_do_something_dumb = \&Dumb::do_something_dumb;
    *Dumb::do_something_dumb = sub { 
        $SIG{__DIE__} = ...
        goto &$dumb_package_do_something_dumb;
    };
    
  • Ou substituir um embutido que sempre chamadas ...

    package Dumb; 
    use subs 'chdir';
    sub chdir { 
        $SIG{__DIE__} = ...
        CORE::chdir @_;
    };
    
  • Se tudo mais falhar, você pode chicotear os olhos do cavalo com isso:

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

Isso vai substituir morrer Globalmente, a única maneira de voltar die é abordar como CORE::die.

Alguma combinação disso funcionará.

Embora mude um die Não morrer tem uma solução específica, como mostrado nas outras respostas; em geral, você sempre pode substituir as sub -rotinas em outros pacotes. Você não muda a fonte original.

Primeiro, carregue o pacote original para obter todas as definições originais. Depois que o original estiver em vigor, você pode redefinir a sub -rotina problemática:

 BEGIN {
      use Original::Lib;

      no warnings 'redefine';

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

Você pode até cortar e colar a definição original e ajustar o que precisa. Não é uma ótima solução, mas se você não pode alterar a fonte original (ou deseja tentar algo antes de alterar o original), ela pode funcionar.

Além disso, você pode copiar o arquivo de origem original em um diretório separado para o seu aplicativo. Como você controla esse diretório, você pode editar os arquivos nele. Você modifica esse copiar e carregá -lo adicionando esse diretório ao caminho de pesquisa de módulos da Perl:

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

Sua cópia permanece por aí, mesmo que alguém atualize o módulo original (embora você precise mesclar alterações).

Eu falo sobre isso um pouco em Dominar perl, onde mostro outras técnicas para fazer esse tipo de coisa. O truque é não quebrar ainda mais as coisas. Como você não quebra as coisas depende do que você está fazendo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top