Загрузка модуля Perl - Защита от: возможно, вы забыли загрузить «Bla»? - PullRequest
5 голосов
/ 07 ноября 2011

Когда вы запускаете perl -e "Bla->new", вы получаете эту общеизвестную ошибку:

Can't locate object method "new" via package "Bla"
(perhaps you forgot to load "Bla"?)

Произошло в процессе Perl-сервера на днях из-за моего недосмотра. Существует несколько сценариев, и большинство из них имеют правильные операторы use. Но был один сценарий, который выполнял Bla->new в sub blub в строке 123, но пропускал use Bla в верхней части, и когда он был нажат щелчком без других скриптов, использующих Bla, загруженных серверный процесс раньше, потом грохнуть!

Тестирование сценария в отдельности было бы очевидным способом защиты от этой конкретной ошибки, но, увы, код зависит от огромной среды. Вы знаете другой способ защиты от этого упущения?

Обновление

Вот один пример того, как PPI (несмотря на его достоинства) ограничен в своем взгляде на Perl:

use strict;
use HTTP::Request::Common;

my $req = GET 'http://www.example.com';
$req->headers->push_header( Bla => time );

my $au=Auweia->new;

__END__
PPI::Token::Symbol          '$req'
PPI::Token::Operator        '->'
PPI::Token::Word            'headers'
PPI::Token::Operator        '->'
PPI::Token::Word            'push_header'

PPI::Token::Symbol          '$au'
PPI::Token::Operator        '='
PPI::Token::Word            'Auweia'
PPI::Token::Operator        '->'
PPI::Token::Word            'new'

Установка заголовка и назначение Auweia->new синтаксического анализа. Поэтому я не уверен, как вы можете опираться на такой шаткий фундамент. Я думаю, проблема в том, что Auweia также может быть подпрограммой; perl.exe не может сказать до времени выполнения.

Дальнейшее обновление

Хорошо, из поучительных комментариев @ Schwern ниже я узнал, что PPI - это просто токенизатор, и вы можете использовать его, если вы принимаете его ограничения.

Ответы [ 2 ]

10 голосов
/ 07 ноября 2011

Тестирование - единственный ответ, достойный усилий. Если код содержит ошибки, такие как забывание загрузить класс, возможно, он содержит другие ошибки Какими бы ни были препятствия, сделайте это проверяемым. В противном случае вы исправляете сито.

Тем не менее, у вас есть два варианта. Вы можете использовать Class :: Autouse , который попытается загрузить модуль, если он еще не загружен. Это удобно, но, поскольку оно влияет на весь процесс, оно может иметь непредвиденные последствия.

Или вы можете использовать PPI для сканирования вашего кода и поиска всех вызовов методов класса. PPI :: Dumper очень удобно, чтобы понять, как PPI видит Perl.

use strict;
use warnings;

use PPI;
use PPI::Dumper;

my $file = shift;
my $doc = PPI::Document->new($file);

# How PPI sees a class method call.
#    PPI::Token::Word      'Class'
#    PPI::Token::Operator  '->'
#    PPI::Token::Word      'method'
$doc->find( sub {
    my($node, $class) = @_;

    # First we want a word
    return 0 unless $class->isa("PPI::Token::Word");

    # It's not a class, it's actually a method call.
    return 0 if $class->method_call;

    my $class_name = $class->literal;

    # Next to it is a -> operator
    my $op = $class->snext_sibling or return 0;
    return 0 unless $op->isa("PPI::Token::Operator") and $op->content eq '->';

    # And then another word which PPI identifies as a method call.
    my $method = $op->snext_sibling or return 0;
    return 0 unless $method->isa("PPI::Token::Word") and $method->method_call;

    my $method_name = $method->literal;

    printf "$class->$method_name seen at %s line %d.\n", $file, $class->line_number;
});
0 голосов
/ 14 ноября 2011

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

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

Информацию о предварительной загрузке в Apache см. В разделе Practical mod_perl

...