Могу ли я изменить строки кода в загруженном модуле в Perl? - PullRequest
5 голосов
/ 06 сентября 2010

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

Что ж, недавно я обнаружил, что просто закомментирую следующие строки кода в Tag.pmодин из модулей зависимостей FLV::Info, например, так:

=pod
if ($datasize < 11)
   {
      die "Tag size is too small ($datasize) at byte " . $file->get_pos(-10);
   }
=cut

FLV::Info с готовностью выполнит работу, как и ожидалось.

Я не уверен, что это оченьтупой вопрос, но мне любопытно:

Есть ли простой способ изменить пару строк кода в загруженном модуле без изменения исходного файла .pm?

Любые идеи, предложения или комментарии?Спасибо, как всегда:)

ОБНОВЛЕНИЕ

Большое спасибо @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 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

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

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

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

Ответы [ 2 ]

18 голосов
/ 06 сентября 2010

Simple? Нет. Но есть некоторые сумасшедшие вещи, которые вы можете сделать. Вот некоторые плохие идеи.

Одним из наиболее очевидных является размещение взломанной копии файла .pm в вашем проекте, где-то так, чтобы его видели до версии системы.

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

use FLV::Tag;

no warnings 'redefine';
*FLV::Tag::parse = sub {
    ...copy of FLV::Tag::parse with your edits...
};

Вы можете переопределить die, чтобы не умереть, когда увидит это сообщение.

BEGIN {
    # In order to override die() later, you must override it at compile time.
    *CORE::GLOBAL::die = sub { return CORE::die(@_) };
}

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

    ...do your thing...
}

Вы можете сбросить содержимое этой подпрограммы обратно в Perl, выполнить замену строки в коде и вернуть ее обратно.

use Data::Dump::Streamer;
my $original = FLV::Tag->can("parse");
my $code = Dump($original)->Out;
$code =~ s{\Qif ($datasize < 11)\E}{if( 0 )};

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

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

Они все плохие идеи. Вам лучше просто поменять код и связаться с автором еще раз, чтобы сообщить им, что есть новая информация.

3 голосов
/ 06 сентября 2010

Хорошо, ответ Шверна был довольно тщательным, но вот один из подходов, который будет менее "плохим" ...

Следуйте его второму подходу (вырезайте и вставляйте всю подпрограмму в свой код и вставляйте ее после загрузки оригинала) ... НО ... обусловливайте ее конкретной версией FLV::Info (или FLV::Tag).

Таким образом, у вас все еще есть патч обезьяны (технически это патч обезьяны?), НО вы удалите одну из главных причин, по которой этот подход будет считаться «плохим», а именно, что любое обновление модуля может конфликт с вашей пользовательской пропатченной подпрограммой. Если вы защищаете перезапись с проверкой версии, вы устраняете эту проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...