Почему try::Tiny try {} отличается от eval {} для объектов, созданных внутри блока try?

У меня есть следующий код инициализации в апплете на панели задач:

use Gtk3 -init;
use Glib::Object::Introspection;

eval {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
        );
};

if ($@) {
    say "no notify because setup failed: $@";
    $use_notify = 0;
} else {
    MyProgram::Notify->init();
}

Этот код основан на fdpowermon, но, похоже, взяты более или менее из примеров обработки исключений в POD Glib:: Объект::Самоанализ.

Но perlcritic (на уровне 3) по этому поводу спорит:

Return value of eval not tested at line …

Поэтому я попытался переписать это с помощью Try::Tiny:

use Gtk3 -init;
use Glib::Object::Introspection;
use Try::Tiny;

try {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
        );
} catch {
    say "no notify because setup failed: $@";
    $use_notify = 0;
} finally {
    if (!$@) {
        MyProgram::Notify->init();
    }
}

Но затем perl возражает:

Can't locate object method "new" via package MyProgram::Notify::Notification

Хотя я вижу, что особенно блок finally не является реальным улучшением, я не понимаю, почему использование Try::Tiny имеет такое значение в отношении пакета, созданного Glib::Object::Introspection.

Или есть лучший способ, чем Try::Tiny, сделать этот код более элегантным и читабельным, оставив perlcritic довольным?


person Axel Beckert    schedule 28.01.2017    source источник
comment
@ikegami: Да, вызов MyProgram::Notify::Notification-›new кажется частью магии Glib::Object::Introspection. Спасибо за пример кода, но он кажется мне еще менее читаемым. :-/   -  person Axel Beckert    schedule 28.01.2017
comment
Но perlcritic (на уровне 3) возражает по этому поводу: возвращаемое значение eval не проверяется в строке… -- зачем вообще нужно проверять возвращаемое значение? Это не предписанный способ проверки исключения и не единственный правильный. Критик также должен был проверить, есть ли какой-либо код между eval, закрывающими }; и if ($@), прежде чем жаловаться. Вы должны идти по perlcritic и по суровому уровню?   -  person zdim    schedule 28.01.2017


Ответы (2)


Весь смысл критики в том, чтобы не проверять $@, потому что это могло быть затерто. Но после всех ваших изменений вы все еще проверяете $@!

Хуже того, Try::Tiny помещает ошибку в $_, а не в $@ и только в catch блоков.

Я думаю, что происходит то, что MyProgram::Notify->init() вызывается, когда этого не должно быть из-за вышеуказанных ошибок.

Исправить:

my $use_notify = 1;
try {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );

    MyProgram::Notify->init();
} catch {
    say "no notify because setup failed: $_";
    $use_notify = 0;
}

or

my $use_notify = 1;
try {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );
} catch {
    say "no notify because setup failed: $_";
    $use_notify = 0;
}

MyProgram::Notify->init() if $use_notify;

Без Try::Tiny:

my $use_notify = 1;
if (!eval {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );

    MyProgram::Notify->init();

    1;  # No exception
}) {
    say "no notify because setup failed: " . ( $@ // "Unknown error" );
    $use_notify = 0;
}

or

my $use_notify = 1;
if (!eval {
    Glib::Object::Introspection->setup(
        basename => 'Notify',
        version => '0.7',
        package => "MyProgram::Notify",
    );
    1;  # No exception
}) {
    say "no notify because setup failed: " . ( $@ // "Unknown error" );
    $use_notify = 0;
}

MyProgram::Notify->init() if $use_notify;
person ikegami    schedule 28.01.2017
comment
Использование $@ в блоке finally происходит из Try::Tiny's POD. - person Axel Beckert; 28.01.2017
comment
Первый вариант я уже пробовал и раньше, и он мне не подошел. Ваш второй пример действительно выглядит как правильное решение. Я устанавливаю $use_notify, но использую его только позже, хотя я мог бы использовать его уже сейчас. - person Axel Beckert; 28.01.2017
comment
Re Использование $@ в блоке finally взято из Try::Tiny's POD. Прочтите ссылку еще раз. Он не говорит ничего отдаленно похожего на это. На самом деле он говорит, что $@ не содержит ошибку., Внутри блока catch перехваченная ошибка хранится в $_, и обратите внимание, что блок finally не локализует $_ с ошибкой. Также прямо упоминается, что $@ вообще не изменяется. - person ikegami; 28.01.2017
comment
Давайте не будем зацикливаться на $@. Мне очень нравятся ваши первые два примера, но ни один из них не работает для меня, а также прерывается с ошибкой Can't locate object method "new" via package "MyProgram::Notify::Notification". Так что мой вопрос, что на самом деле вызывает разницу, остается в силе. Думаю, сначала мне нужно понять, откуда взялся этот объект. Только что проверил с помощью printf-debugging: MyProgram::Notify->init(); в исходном коде успешно вызывается, т.е. там работает. - person Axel Beckert; 28.01.2017

На самом деле ответ на мой вопрос в основном такой же, как этот: отсутствует точка с запятой за блоком catch (или, скорее, finally) .

Извините за шум.

person Axel Beckert    schedule 28.01.2017
comment
Это было бы легче определить, если бы вы включили больше кода. Поскольку закрывающая фигура выглядела как последняя строка программы, это не должно было иметь значения. - person simbabque; 28.01.2017