Можно ли изменить строки кода в загруженном модуле в Perl?

Когда я использую модуль FLV :: Info для извлечения метаданных или объединения нескольких файлов FLV, я часто получаю сообщение об ошибке «Размер тега слишком мал», и тогда модуль просто отказывайся работать. Кто-то опубликовал отчет об ошибке здесь три года назад, но, похоже, нет исправления.

Что ж, Спасибо, как всегда :)

ОБНОВЛЕНИЕ

Большое спасибо @Shwern. Ваш ответ очень радует :) Также спасибо @DVK за предложение и термин «monkey patch» и @brian за рекомендацию к книге.

Вот мой отзыв о тестах на примере FLV-файла, который бросил бы мне «Размер тега» ошибка слишком мала, если я использую оригинальный модуль, ничего не делая с ним.

Подход «eval it back» решает проблему

use FLV::Info;

use Data::Dump::Streamer;
my $original = FLV::Tag->can("parse");
my $code = Dump($original)->Out;
#$code =~ s{\Qif ($datasize < 11)\E}{if (0)}; #This somehow won't work
$code =~ s{die "Tag}{warn "Tag}; #Let it warn but not die

no warnings 'redefine';
*FLV::Tag::parse = eval $code;

my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();

Подход «overide die to not die» также работает

BEGIN {
    *CORE::GLOBAL::die = sub { return CORE::die(@_) };
}
use FLV::Info;

{
    local *CORE::GLOBAL::die = sub {
         return if $_[0] =~ /^Tag size is too small/;
         return CORE::die(@_);
};

my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();
}

«Переопределение redefine» "Подход, однако, не работает так, как я ожидал.

Я скопировал и вставил оригинальную подпрограмму FLV :: Tag :: parse и закомментировал строки кода точно так же, как я изменил исходный файл Tag.pm, как итак:

use FLV::Info;
no warnings 'redefine';
*FLV::Tag::parse = sub {
    ...
    ...
=pod
   if ($datasize < 11)
   {
      die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10);
   }
=cut
   ...
   ...
};

my $reader = FLV::Info->new();
$reader->parse('sample.flv');
my %info = $reader->get_info();
print "$info{video_count} video frames\n";
print $reader->report();

но я получил эту ошибку:

Unknown tag type 18 at byte 13 (0xd)

Ну, даже если копировать и вставлять точно такую ​​же подпрограмму синтаксического анализа без каких-либо изменений в моем переопределении, я получаю ошибку «Неизвестный тип тега» вместо «Размер тега слишком мал».

Это странно!

Для справки, подходы «eval it back» и «override die to not die» приведут меня к следующему:

1992 video frames
File name                sample.flv
File size                5767831 bytes
Duration                 about 79.6 seconds
Video                    1992 frames
  codec                  AVC
  type                   interframe/keyframe
Audio                    1712 packets
  format                 AAC
  rate                   44100 Hz
  size                   16 bit
  type                   stereo
Meta                     1 event
  audiocodecid           10
  audiosamplerate        22050
  audiosamplesize        16
  audiosize              342817
  creationdate           unknown
  datasize               805
  duration               79.6
  filesize               5767869
  framerate              25
  height                 300
  keyframes              {
    >>>                    'filepositions' => [
    >>>                                         '780',
    >>>                                         '865',
    >>>                                         '1324122',
    >>>                                         '2348913',
    >>>                                         '2978630',
    >>>                                         '3479001',
    >>>                                         '3973756',
    >>>                                         '4476281',
    >>>                                         '4997226',
    >>>                                         '5391890'
    >>>                                       ],
    >>>                    'times' => [
    >>>                                 '0',
    >>>                                 '0',
    >>>                                 '9.6',
    >>>                                 '19.2',
    >>>                                 '28.8',
    >>>                                 '38.4',
    >>>                                 '46.32',
    >>>                                 '55.92',
    >>>                                 '64.88',
    >>>                                 '73.88'
    >>>                               ]
    >>>                  }
  lastkeyframetimestamp  73.88
  lasttimestamp          79.6
  metadatacreator        Manitu Group FLV MetaData Injector 2
  metadatadate           1281964633858
  stereo                 1
  videocodecid           7
  videosize              5424234
  width                  400

FINAL UPDATE

Я выяснил, почему подход «redefine» потерпел неудачу при включении строгие и предупреждения прагма. Спасибо @Schwern за напоминание:)

Сначала добавьте следующие строки кода (скопированные из модуля FLV :: Util), а затем выполните переопределение подпрограммы FLV :: Tag :: parse.

Readonly::Hash our %TAG_CLASSES => (
   8  => 'FLV::AudioTag',
   9  => 'FLV::VideoTag',
   18 => 'FLV::MetaTag',
);

5
задан Mike 8 September 2010 в 06:35
поделиться