Como posso contornar uma chamada 'Die' em uma biblioteca Perl que não posso modificar?
-
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?
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 substituindothat
.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.