Spec-Zone.ru › Perl 5.32

предупреждения

СОДЕРЖАНИЕ

  • ИМЯ
  • СИНТАКСИС
  • ОПИСАНИЕ
    • Стандартные предупреждения и необязательные предупреждения
    • Что не так с -w и $^W
    • Управление предупреждениями из командной строки
    • Обратная совместимость
    • Иерархия категорий
    • Критические предупреждения
    • Отчет о предупреждениях из модуля
  • ФУНКЦИИ

ИМЯ

warnings - Perl-pragma для управления необязательными предупреждениями

СИНТАКСИС

use warnings;
no warnings;

use warnings "all";
no warnings "all";

use warnings::register;
if (warnings::enabled()) {
    warnings::warn("some warning");
}

if (warnings::enabled("void")) {
    warnings::warn("void", "some warning");
}

if (warnings::enabled($object)) {
    warnings::warn($object, "some warning");
}

warnings::warnif("some warning");
warnings::warnif("void", "some warning");
warnings::warnif($object, "some warning");

ОПИСАНИЕ

Pragma warnings дает возможность контролировать, какие предупреждения включены в различных частях Perl-программы. Это более гибкая альтернатива как флагу командной строки -w, так и эквивалентной Perl-переменной $^W.

Этот pragma работает точно так же, как strict pragma. Это означает, что область действия pragma предупреждений ограничена вложенным блоком. Это также означает, что настройка pragma не будет "просачиваться" в другие файлы (через use, require или do). Это позволяет авторам независимо определять степень проверок предупреждений, которые будут применяться к их модулю.

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

Все предупреждения включены в блоке с помощью одного из этих способов:

use warnings;
use warnings 'all';

Аналогично, все предупреждения отключаются в блоке с помощью одного из этих способов:

no warnings;
no warnings 'all';

Например, рассмотрим код ниже:

use warnings;
my @a;
{
    no warnings;
    my $b = @a[0];
}
my $c = @a[0];

В блоке-контейнере включены предупреждения, но во внутреннем блоке они отключены. В этом случае это означает, что присваивание скаляру $c вызовет предупреждение "Scalar value @a[0] better written as $a[0]", но присваивание скаляру $b нет.

Стандартные предупреждения и необязательные предупреждения

Перед введением лексических предупреждений в Perl существовало две категории предупреждений: обязательные и необязательные.

Как следует из названия, если ваш код вызывал обязательное предупреждение, вы получали предупреждение независимо от вашего желания. Например, код ниже всегда будет генерировать предупреждение "isn't numeric" о "2:".

my $a = "2:" + 3;

С введением лексических предупреждений обязательные предупреждения теперь становятся стандартными предупреждениями. Разница в том, что, хотя ранее обязательные предупреждения по-прежнему включены по умолчанию, их можно впоследствии включать или отключать с помощью лексического pragma предупреждений. Например, в коде ниже, предупреждение "isn't numeric" будет отображаться только для переменной $a.

my $a = "2:" + 3;
no warnings;
my $b = "2:" + 3;

Обратите внимание, что ни флаг -w, ни $^W нельзя использовать для отключения/включения стандартных предупреждений. В этом случае они по-прежнему обязательны.

Что не так с -w и $^W

Хотя очень полезный, большая проблема с использованием -w в командной строке для включения предупреждений заключается в том, что это все или ничего. Возьмем типичную ситуацию, когда вы пишете Perl-программу. Часть кода вы напишете сами, но очень вероятно, что вы будете использовать готовые Perl-модули. Если вы в этом случае используете флаг -w, вы включите предупреждения в части кода, который вы не написали.

Аналогично, использование $^W для отключения или включения блоков кода фундаментально ошибочно. Для начала, скажем, вы хотите отключить предупреждения в блоке кода. Вы можете ожидать, что этого будет достаточно:

{
    local ($^W) = 0;
    my $a =+ 2;
    my $b; chop $b;
}

При запуске этого кода с флагом -w будет выдано предупреждение для строки $a: "Reversed += operator".

Проблема в том, что Perl имеет предупреждения как на этапе компиляции, так и на этапе выполнения. Для отключения предупреждений на этапе компиляции вам необходимо переписать код следующим образом:

{
    BEGIN { $^W = 0 }
    my $a =+ 2;
    my $b; chop $b;
}

И обратите внимание, что в отличие от первого примера, это навсегда установит $^W, поскольку оно не может выполняться как на этапе компиляции, так и быть локализованным в блоке выполнения.

Другая большая проблема с $^W заключается в том, как вы можете непреднамеренно изменить настройку предупреждения в неожиданных местах вашего кода. Например, при запуске кода ниже (без флага -w) второй вызов doit вызовет предупреждение "Use of uninitialized value", а первый нет.

sub doit
{
    my $b; chop $b;
}

doit();

{
    local ($^W) = 1;
    doit()
}

Это побочный эффект динамического области действия $^W.

Лексические предупреждения устраняют эти ограничения, предоставляя более точный контроль над тем, где предупреждения могут или не могут быть активированы.

Управление предупреждениями из командной строки

Существуют три флага командной строки, которые можно использовать для управления тем, когда генерируются (или нет) предупреждения:

-w

Это существующий флаг. Если лексический pragma предупреждений не используется ни в одном из ваших кодов или модулей, которые вы используете, этот флаг включит предупреждения везде. Подробности о взаимодействии этого флага с лексическими предупреждениями см. в "Обратная совместимость".

-W

Если в командной строке используется флаг -W, он включит все предупреждения в программе независимо от того, были ли предупреждения локально отключены с помощью no warnings или $^W =0. Это включает все файлы, которые подключаются через use, require или do. Представьте его как Perl-аналог команды "lint".

-X

Делает ровно противоположное флагу -W, т.е. отключает все предупреждения.

Обратная совместимость

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

Как лексические предупреждения взаимодействуют с -w/$^W:

  1. Если ни один из трех флагов командной строки (-w, -W или -X), которые управляют предупреждениями, не используется, и ни $^W, ни warnings pragma не используются, то стандартные предупреждения будут включены, а необязательные - отключены. Это означает, что устаревший код, не пытающийся управлять предупреждениями, будет работать без изменений.

  2. Флаг -w просто устанавливает глобальную переменную $^W, как в 5.005. Это означает, что любой устаревший код, который в настоящее время полагается на манипулирование $^W для управления поведением предупреждений, по-прежнему будет работать как есть.

  3. Помимо того, что теперь это булево значение, переменная $^W работает точно так же, ужасно и неуправляемо, глобально, за исключением того, что она не может отключать/включать стандартные предупреждения.

  4. Если часть кода находится под управлением pragma warnings, как переменная $^W, так и флаг -w будут игнорироваться в рамках области действия лексического предупреждения.

  5. Единственный способ переопределить настройку лексических предупреждений — это флаги командной строки -W или -X.

Совместный эффект 3 и 4 заключается в том, что это позволит коду, который использует warnings pragma, управлять поведением предупреждений кода типа $^W (используя local $^W=0), если он действительно хочет этого, но не наоборот.

Иерархия категорий

Была определена иерархия "категорий", позволяющая группам предупреждений включаться/отключаться изолированно.

Текущая иерархия:

all -+
     |
     +- closure
     |
     +- deprecated
     |
     +- exiting
     |
     +- experimental --+
     |                 |
     |                 +- experimental::alpha_assertions
     |                 |
     |                 +- experimental::bitwise
     |                 |
     |                 +- experimental::const_attr
     |                 |
     |                 +- experimental::declared_refs
     |                 |
     |                 +- experimental::isa
     |                 |
     |                 +- experimental::lexical_subs
     |                 |
     |                 +- experimental::postderef
     |                 |
     |                 +- experimental::private_use
     |                 |
     |                 +- experimental::re_strict
     |                 |
     |                 +- experimental::refaliasing
     |                 |
     |                 +- experimental::regex_sets
     |                 |
     |                 +- experimental::script_run
     |                 |
     |                 +- experimental::signatures
     |                 |
     |                 +- experimental::smartmatch
     |                 |
     |                 +- experimental::uniprop_wildcards
     |                 |
     |                 +- experimental::vlb
     |                 |
     |                 +- experimental::win32_perlio
     |
     +- glob
     |
     +- imprecision
     |
     +- io ------------+
     |                 |
     |                 +- closed
     |                 |
     |                 +- exec
     |                 |
     |                 +- layer
     |                 |
     |                 +- newline
     |                 |
     |                 +- pipe
     |                 |
     |                 +- syscalls
     |                 |
     |                 +- unopened
     |
     +- locale
     |
     +- misc
     |
     +- missing
     |
     +- numeric
     |
     +- once
     |
     +- overflow
     |
     +- pack
     |
     +- portable
     |
     +- recursion
     |
     +- redefine
     |
     +- redundant
     |
     +- regexp
     |
     +- severe --------+
     |                 |
     |                 +- debugging
     |                 |
     |                 +- inplace
     |                 |
     |                 +- internal
     |                 |
     |                 +- malloc
     |
     +- shadow
     |
     +- signal
     |
     +- substr
     |
     +- syntax --------+
     |                 |
     |                 +- ambiguous
     |                 |
     |                 +- bareword
     |                 |
     |                 +- digit
     |                 |
     |                 +- illegalproto
     |                 |
     |                 +- parenthesis
     |                 |
     |                 +- precedence
     |                 |
     |                 +- printf
     |                 |
     |                 +- prototype
     |                 |
     |                 +- qw
     |                 |
     |                 +- reserved
     |                 |
     |                 +- semicolon
     |
     +- taint
     |
     +- threads
     |
     +- uninitialized
     |
     +- unpack
     |
     +- untie
     |
     +- utf8 ----------+
     |                 |
     |                 +- non_unicode
     |                 |
     |                 +- nonchar
     |                 |
     |                 +- surrogate
     |
     +- void

Как и pragma "strict", любое из этих категорий можно комбинировать

use warnings qw(void redefine);
no warnings qw(io syntax untie);

Также как и pragma "strict", если в данной области существует более одного экземпляра pragma warnings, накопительный эффект является аддитивным.

use warnings qw(void); # only "void" warnings enabled
...
use warnings qw(io);   # only "void" & "io" warnings enabled
...
no warnings qw(void);  # only "io" warnings enabled

Чтобы определить, к какой категории относится конкретное предупреждение, см. perldiag.

Примечание: До Perl 5.8.0 лексическая категория предупреждений "deprecated" была подкатегорией категории "syntax". Сейчас она является категорией верхнего уровня.

Примечание: До 5.21.0 лексическая категория предупреждений "missing" была внутренне определена как эквивалентная категории "uninitialized". Сейчас она является категорией верхнего уровня.

Критические предупреждения

Наличие слова "FATAL" в списке категорий повысит предупреждения в этих категориях до критических ошибок в данной лексической области.

ПРИМЕЧАНИЕ: Критические предупреждения следует использовать с осторожностью, особенно FATAL => 'all'.

Библиотеки, использующие warnings::warn для пользовательских категорий предупреждений, обычно не ожидают, что warnings::warn будет критическим, и в результате могут оказаться в непредвиденном состоянии. Для XS-модулей, выдавающих категоризированные предупреждения, такие непредвиденные исключения также могут раскрыть ошибки утечки памяти.

Кроме того, сам Perl-интерпретатор имел серьезные ошибки, связанные с критическими предупреждениями. Подведение итогов по решенным и нерешенным проблемам на январь 2015 года можно найти в данном сообщении perl5-porters.

Хотя некоторые разработчики считают фатализацию некоторых предупреждений полезной техникой защищенного программирования, использование FATAL => 'all' для фатализации всех возможных категорий предупреждений — включая пользовательские — особенно рискованно. Поэтому использование FATAL => 'all' не рекомендуется.

Модуль strictures на CPAN предоставляет один пример набора предупреждений, который, по мнению авторов модуля, относительно безопасно фатализировать.

END_OF_DOCUMENT_MARKER

ПРИМЕЧАНИЕ: пользователи предупреждений FATAL, особенно те, кто использует FATAL => 'all', должны полностью осознавать, что они рискуют будущей переносимостью своих программ, делая это. Perl не дает никаких гарантий, что не будут введены новые предупреждения или категории предупреждений в будущем; на самом деле, мы прямо заявляем о своём праве это сделать. Код, который может не предупреждать сейчас, может предупреждать в будущей версии Perl, если команда разработчиков Perl5 посчитает это в наилучших интересах сообщества. Если код, использующий предупреждения FATAL, сломается из-за введения нового предупреждения, мы НЕ будем рассматривать это как несовместимое изменение. Пользователи предупреждений FATAL должны проявлять особую осторожность при обновлении, чтобы проверить, не вызывает ли их код новых предупреждений, и должны обращать особое внимание на мелкие детали документации используемых функций, чтобы убедиться, что они не используют функции, которые задокументированы как рискованные, устаревшие или не определённые, или где в документации говорится «так что этого не делайте», или что-то в том же духе и смысле. Использование таких функций в сочетании с предупреждениями FATAL ВЕСЬМА НА РИСК ПОЛЬЗОВАТЕЛЯ.

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

В коде ниже использование time, length и join может привести к предупреждению "Useless use of xxx in void context".

use warnings;

time;

{
    use warnings FATAL => qw(void);
    length "abc";
}

join "", 1,2,3;

print "done\n";

При запуске он выдает этот вывод

Useless use of time in void context at fatal line 3.
Useless use of length in void context at fatal line 7.

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

Чтобы явно отключить предупреждение «FATAL», просто отключите предупреждение, с которым оно связано. Например, чтобы отключить предупреждение «void» в приведённом выше примере, можно использовать любой из этих способов:

no warnings qw(void);
no warnings FATAL => qw(void);

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

use warnings FATAL => 'all', NONFATAL => 'syntax';

Начиная с Perl 5.20, вместо use warnings FATAL => 'all'; можно использовать:

use v5.20;       # Perl 5.20 or greater is required for the following
use warnings 'FATAL';  # short form of "use warnings FATAL => 'all';"

Если вы хотите, чтобы ваша программа была совместима с версиями Perl до 5.20, вы должны использовать use warnings FATAL => 'all'; вместо этого. (В предыдущих версиях Perl поведение операторов use warnings 'FATAL';, use warnings 'NONFATAL'; и no warnings 'FATAL'; не было определено; они не вели себя так, как будто в них включена часть => 'all'. Начиная с версии 5.20, они ведут себя так.)

Сообщения о предупреждениях из модуля

Предикат warnings предоставляет ряд функций, полезных для авторов модулей. Они используются, когда вы хотите сообщить предупреждение, специфичное для модуля, вызывающему модулю, который включил предупреждения с помощью предикатов warnings.

Рассмотрим модуль MyMod::Abc ниже.

package MyMod::Abc;

use warnings::register;

sub open {
    my $path = shift;
    if ($path !~ m#^/#) {
        warnings::warn("changing relative path to /var/abc")
            if warnings::enabled();
        $path = "/var/abc/$path";
    }
}

1;

Вызов функции warnings::register создаст новую категорию предупреждений под названием «MyMod::Abc», то есть новое имя категории соответствует текущему имени пакета. Функция open в модуле отобразит сообщение об ошибке, если ей будет передан относительный путь в качестве параметра. Это предупреждение будет отображено только в том случае, если код, использующий MyMod::Abc, фактически включил их с помощью предикатов warnings, как показано ниже.

use MyMod::Abc;
use warnings 'MyMod::Abc';
...
abc::open("../fred.txt");

Также возможно проверить, установлены ли предопределённые категории предупреждений в вызывающем модуле с помощью функции warnings::enabled. Рассмотрим этот фрагмент кода:

package MyMod::Abc;

sub open {
    if (warnings::enabled("deprecated")) {
        warnings::warn("deprecated",
                       "open is deprecated, use new instead");
    }
    new(@_);
}

sub new
...
1;

Функция open устарела, поэтому был включён код для отображения сообщения об ошибке всякий раз, когда в вызывающем модуле включена (по крайней мере) категория предупреждений «deprecated». Нечто вроде этого, скажем.

use warnings 'deprecated';
use MyMod::Abc;
...
MyMod::Abc::open($filename);

Для фактического отображения сообщения об ошибке следует использовать либо функцию warnings::warn, либо функцию warnings::warnif. Это связано с тем, что они могут использовать функцию, которая позволяет повышать предупреждения до критических ошибок. Таким образом, в данном случае

use MyMod::Abc;
use warnings FATAL => 'MyMod::Abc';
...
MyMod::Abc::open('../fred.txt');

функция warnings::warnif обнаружит это и завершится после отображения сообщения об ошибке.

Три функции предупреждения, warnings::warn, warnings::warnif и warnings::enabled, могут дополнительно принимать ссылку на объект вместо имени категории. В этом случае функции будут использовать имя класса объекта в качестве категории предупреждений.

Рассмотрим этот пример:

package Original;

no warnings;
use warnings::register;

sub new
{
    my $class = shift;
    bless [], $class;
}

sub check
{
    my $self = shift;
    my $value = shift;

    if ($value % 2 && warnings::enabled($self))
      { warnings::warn($self, "Odd numbers are unsafe") }
}

sub doit
{
    my $self = shift;
    my $value = shift;
    $self->check($value);
    # ...
}

1;

package Derived;

use warnings::register;
use Original;
our @ISA = qw( Original );
sub new
{
    my $class = shift;
    bless [], $class;
}


1;

Код ниже использует оба модуля, но включает только предупреждения из Derived.

use Original;
use Derived;
use warnings 'Derived';
my $a = Original->new();
$a->doit(1);
my $b = Derived->new();
$a->doit(1);

При выполнении этого кода только объект Derived, $b, сгенерирует предупреждение.

Odd numbers are unsafe at main.pl line 7

Обратите также внимание, что предупреждение сообщается в строке, где объект используется впервые.

При регистрации новых категорий предупреждений можно добавить больше имён в warnings::register, как в этом примере:

package MyModule;
use warnings::register qw(format precision);

...

warnings::warnif('MyModule::format', '...');

ФУНКЦИИ

Примечание: функции с именами, оканчивающимися на _at_level, были добавлены в Perl 5.28.

use warnings::register

Создаёт новую категорию предупреждений с тем же именем, что и пакет, в котором используется предикат.

warnings::enabled()

Используйте категорию предупреждений с тем же именем, что и текущий пакет.

Возвращает ИСТИНА, если эта категория предупреждений включена в вызывающем модуле. В противном случае возвращает ЛОЖЬ.

warnings::enabled($category)

Возвращает ИСТИНА, если категория предупреждений $category включена в вызывающем модуле. В противном случае возвращает ЛОЖЬ.

warnings::enabled($object)

Использует имя класса для ссылки на объект, $object, в качестве категории предупреждений.

Возвращает ИСТИНА, если эта категория предупреждений включена в первом области видимости, где используется объект. В противном случае возвращает ЛОЖЬ.

warnings::enabled_at_level($category, $level)

Как и warnings::enabled, но $level указывает точную рамку вызова, 0 — непосредственный вызывающий.

warnings::fatal_enabled()

Возвращает ИСТИНА, если категория предупреждений с тем же именем, что и текущий пакет, установлена в FATAL в вызывающем модуле. В противном случае возвращает ЛОЖЬ.

warnings::fatal_enabled($category)

Возвращает ИСТИНА, если категория предупреждений $category установлена в FATAL в вызывающем модуле. В противном случае возвращает ЛОЖЬ.

warnings::fatal_enabled($object)

Использует имя класса для ссылки на объект, $object, в качестве категории предупреждений.

Возвращает ИСТИНА, если эта категория предупреждений установлена в FATAL в первой области видимости, где используется объект. В противном случае возвращает ЛОЖЬ.

warnings::fatal_enabled_at_level($category, $level)

Как и warnings::fatal_enabled, но $level указывает точную рамку вызова, 0 — непосредственный вызывающий.

warnings::warn($message)

Выводит $message в STDERR.

Использует категорию предупреждений с тем же именем, что и текущий пакет.

Если для этой категории предупреждений установлено значение «FATAL» в вызывающем модуле, завершается выполнение. В противном случае возвращается.

warnings::warn($category, $message)

Выводит $message в STDERR.

Если для категории предупреждений $category установлено значение «FATAL» в вызывающем модуле, завершается выполнение. В противном случае возвращается.

warnings::warn($object, $message)

Выводит $message в STDERR.

Использует имя класса для ссылки на объект, $object, в качестве категории предупреждений.

Если для этой категории предупреждений установлено значение «FATAL» в области видимости, где $object используется впервые, завершается выполнение. В противном случае возвращается.

warnings::warn_at_level($category, $level, $message)

Как и warnings::warn, но $level указывает точную рамку вызова, 0 — непосредственный вызывающий.

warnings::warnif($message)

Эквивалентно:

if (warnings::enabled())
  { warnings::warn($message) }
warnings::warnif($category, $message)

Эквивалентно:

if (warnings::enabled($category))
  { warnings::warn($category, $message) }
warnings::warnif($object, $message)

Эквивалентно:

if (warnings::enabled($object))
  { warnings::warn($object, $message) }
warnings::warnif_at_level($category, $level, $message)

Как и warnings::warnif, но $level указывает точную рамку вызова, 0 — непосредственный вызывающий.

warnings::register_categories(@names)

Это регистрирует категории предупреждений для заданных имён и предназначено в основном для использования с предикатом warnings::register.

См. также "Pragmatic Modules" в perlmodlib и perldiag.

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.32.0/warnings

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API