Как мне обойти вызов die в библиотеке Perl, которую я не могу изменить?

Да, проблема связана с библиотекой, которую я использую, и нет, я не могу ее изменить. Мне нужен обходной путь.

По сути, я имею дело с плохо написанной библиотекой Perl, которая завершается с 'die', когда при чтении файла возникает определенная ошибка. Я вызываю эту процедуру из программы, которая просматривает тысячи файлов, несколько из которых плохие. Плохие файлы случаются; Я просто хочу, чтобы моя процедура регистрировала ошибку и продолжала.

Если бы я мог изменить библиотеку, я бы просто изменил

die "error";

к

print "error";return;

, но я не могу. Можно ли как-нибудь составить программу, чтобы плохие файлы не приводили к сбою всего процесса?

СЛЕДУЮЩИЙ ВОПРОС: Использование "eval" для записи вызова, склонного к сбоям, работает хорошо, но как мне настроить обработку ошибок, которые можно отловить в этой среде? Описать:

У меня есть подпрограмма, которая вызывает библиотеку, которая аварийно завершает работу, иногда много раз. Вместо того, чтобы объединять каждый вызов в этой подпрограмме с помощью eval{}, я просто позволяю ему умереть и использую eval {} на уровне, который вызывает мою подпрограмму:

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

Тем не менее, есть ошибки, которые я могу поймать и уловить в функции (). Какой самый правильный / элегантный способ спроектировать перехват ошибок в подпрограмме и подпрограмме вызова так, чтобы я получал правильное поведение как для обнаруженных, так и для необнаруженных ошибок?

3 ответа

Решение

Вы могли бы обернуть это в eval, Увидеть:

perldoc -f eval

Например, вы можете написать:

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

Это превратит фатальную ошибку в предупреждение, более или менее то, что вы предложили. Если die называется, $@ содержит строку, переданную ему.

Это ловушка $SIG{__DIE__}? Если это так, то это более локально, чем вы. Но есть пара стратегий:

  • Вы можете вызвать его пакет и переопределить 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 @_;
       }
    } 
    
  • Если нет, можете поймать его в ловушку. (ищите $SIG на странице, уценка не обрабатывает полную ссылку.)

    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;
    
  • Возможно, вам придется отсканировать библиотеку, найти подпрограмму, которую она всегда вызывает, и использовать ее для загрузки $SIG обработчик путем переопределения that,

    my $dumb_package_do_something_dumb = \&Dumb::do_something_dumb;
    *Dumb::do_something_dumb = sub { 
        $SIG{__DIE__} = ...
        goto &$dumb_package_do_something_dumb;
    };
    
  • Или переопределить встроенный, который он всегда вызывает...

    package Dumb; 
    use subs 'chdir';
    sub chdir { 
        $SIG{__DIE__} = ...
        CORE::chdir @_;
    };
    
  • Если все остальное терпит неудачу, вы можете хлестать глаза лошади с этим:

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

Это переопределит смерть во всем мире, единственный способ вернуть die это решить как CORE::die,

Некоторая комбинация этого будет работать.

Хотя меняя die чтобы не умереть, есть конкретное решение, как показано в других ответах, в общем, вы всегда можете переопределить подпрограммы в других пакетах. Вы не меняете первоначальный источник вообще.

Сначала загрузите оригинальный пакет, чтобы получить все оригинальные определения. Как только оригинал будет на месте, вы можете переопределить проблемную подпрограмму:

 BEGIN {
      use Original::Lib;

      no warnings 'redefine';

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

Вы можете даже вырезать и вставить оригинальное определение и настроить то, что вам нужно. Это не очень хорошее решение, но если вы не можете изменить исходный код (или хотите что-то попробовать, прежде чем сменить оригинал), это может сработать.

Кроме того, вы можете скопировать исходный файл в отдельный каталог для вашего приложения. Поскольку вы управляете этим каталогом, вы можете редактировать файлы в нем. Вы изменяете эту копию и загружаете ее, добавляя эту директорию в путь поиска модулей Perl:

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

Ваша копия остается на месте, даже если кто-то обновляет исходный модуль (хотя вам может потребоваться объединить изменения).

Я довольно много об этом говорю в Мастеринге Perl, где я показываю некоторые другие методы для такого рода вещей. Хитрость в том, чтобы не сломать вещи еще больше. То, как вы не сломаете вещи, зависит от того, что вы делаете.

Другие вопросы по тегам