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

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

  •  19-08-2019
  •  | 
  •  

Вопрос

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

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

ЕСЛИ БЫ я МОГ изменить библиотеку, я бы просто изменил

die "error";

к a

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

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

Это было полезно?

Решение

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

    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 глобально, единственный способ, которым вы можете вернуться 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 , где я показываю некоторые другие приемы Что-то в этом роде. Хитрость в том, чтобы не сломать вещи еще больше. То, как вы не сломаете вещи, зависит от того, что вы делаете.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top