Предупреждения
СОДЕРЖАНИЕ
ИМЯ
warnings — Perl-прагма для управления необязательными предупреждениями
СИНТАКСИС
use warnings;
no warnings;
# Standard warnings are enabled by use v5.35 or above
use v5.35;
use warnings "all";
no warnings "uninitialized";
# or equivalent to those last two ...
use warnings qw(all -uninitialized);
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"); ОПИСАНИЕ
Прагма warnings позволяет управлять предупреждениями, которые активированы в разных частях Perl-программы. Это более гибкая альтернатива как флагу командной строки -w, так и эквивалентной Perl-переменной, $^W.
Эта прагма работает так же, как прагма strict. Это означает, что область действия прагмы предупреждения ограничена содержащим её блоком. Это также означает, что настройки прагмы не будут «просачиваться» в другие файлы (через use, require или do). Это позволяет авторам независимо определять уровень проверок предупреждений, которые будут применяться к их модулю.
По умолчанию, необязательные предупреждения отключены, поэтому любой устаревший код, который не пытается управлять предупреждениями, будет работать без изменений.
Все предупреждения включаются в блоке любым из следующих способов:
use warnings;
use warnings 'all'; Аналогично, все предупреждения отключаются в блоке любым из следующих способов:
no warnings;
no warnings 'all'; Например, рассмотрим код ниже:
use warnings;
my @x;
{
no warnings;
my $y = @x[0];
}
my $z = @x[0]; В блоке предупреждения включены, но в внутреннем блоке они отключены. В данном случае это означает, что присваивание скаляру $z вызовет предупреждение "Scalar value @x[0] better written as $x[0]", но присваивание скаляру $y – нет.
Все предупреждения автоматически включаются в пределах действия объявления use v5.35 (или более высокого уровня).
Стандартные предупреждения и необязательные предупреждения
До появления лексических предупреждений, в Perl было две категории предупреждений: обязательные и необязательные.
Как следует из названия, если ваш код вызывал обязательное предупреждение, то вы получали предупреждение вне зависимости от желания. Например, код ниже всегда будет выдавать предупреждение "isn't numeric" о «2:».
my $x = "2:" + 3; С появлением лексических предупреждений, обязательные предупреждения теперь стали стандартными предупреждениями. Разница заключается в том, что хотя ранее обязательные предупреждения по-прежнему включены по умолчанию, их затем можно включить или отключить с помощью лексической прагмы предупреждений. Например, в коде ниже предупреждение "isn't numeric" будет выдано только для переменной $x.
my $x = "2:" + 3;
no warnings;
my $y = "2:" + 3; Обратите внимание, что ни флаг -w, ни $^W не могут использоваться для отключения/включения стандартных предупреждений. В этом случае они остаются обязательными.
«Отрицательные предупреждения»
Для удобства, вы можете (начиная с Perl 5.34) передавать аргументы в метод import() как положительно, так и отрицательно. Отрицательные предупреждения — это те, у которых перед именем стоит знак -; положительные — всё остальное. Это позволяет включить некоторые предупреждения и отключить другие в одной команде. Таким образом, если вы уже включили множество предупреждений, но хотите немного их подкорректировать в каком-то блоке, можно сделать так:
{
use warnings qw(uninitialized -redefine);
...
} что эквивалентно:
{
use warnings qw(uninitialized);
no warnings qw(redefine);
...
} Список аргументов обрабатывается в указанном порядке. Например, если вы не хотите получать предупреждения об использовании экспериментальных функций, за исключением somefeature, которые вам действительно не нравятся, вы можете сказать так:
use warnings qw(all -experimental experimental::somefeature); что эквивалентно:
use warnings 'all';
no warnings 'experimental';
use warnings 'experimental::somefeature'; Что не так с -w и $^W
Несмотря на свою полезность, главная проблема использования -w в командной строке для включения предупреждений заключается в том, что это «всё или ничего». Рассмотрим типичный сценарий при написании Perl-программы. Части кода вы будете писать сами, но весьма вероятно, что вы будете использовать готовые Perl-модули. Если в этом случае вы используете флаг -w, то вы включаете предупреждения в участках кода, которые вы не писали.
Аналогично, использование $^W для отключения или включения блоков кода принципиально неверно. Начнём с того, что если вы хотите отключить предупреждения в блоке кода, то вы можете ожидать, что это сработает:
{
local ($^W) = 0;
my $x =+ 2;
my $y; chop $y;
} При запуске этого кода с флагом -w будет выдано предупреждение для строки %%%CODE_BLOCK_41%%: %%%CODE_BLOCK_42%%.
Проблема заключается в том, что Perl имеет как предупреждения на этапе компиляции, так и на этапе выполнения. Для отключения предупреждений на этапе компиляции нужно переписать код так:
{
BEGIN { $^W = 0 }
my $x =+ 2;
my $y; chop $y;
} И обратите внимание, что в отличие от первого примера, это постоянно установит значение $^W, так как оно не может выполняться во время компиляции и быть локализованным в блок выполнения.
Другая большая проблема с $^W заключается в том, что вы можете непреднамеренно изменить настройку предупреждений в неожиданных местах вашего кода. Например, когда код ниже выполняется (без флага -w), второй вызов doit вызовет предупреждение "Use of uninitialized value", а первый — нет.
sub doit
{
my $y; chop $y;
}
doit();
{
local ($^W) = 1;
doit()
} Это следствие того, что $^W имеет динамический объём.
Лексические предупреждения позволяют избежать этих ограничений, предоставляя более точный контроль над тем, где можно или нельзя вызывать предупреждения.
Управление предупреждениями из командной строки
Существует три флага командной строки, которые могут быть использованы для управления тем, когда выдаются (или не выдаются) предупреждения:
- -w
-
Это существующий флаг. Если лексическая прагма предупреждений не используется ни в одном из ваших кодов, ни в одном из используемых вами модулей, то этот флаг включит предупреждения везде. Подробнее о том, как этот флаг взаимодействует с лексическими предупреждениями, см. Обратная совместимость.
- -W
-
Если используется флаг -W в командной строке, то он включит все предупреждения во всей программе, независимо от того, были ли предупреждения отключены локально с помощью
no warningsили$^W =0. Это включает все файлы, которые включаются черезuse,requireилиdo. Подумайте об этом как о Perl-эквиваленте команды «lint». - -X
-
Делает ровно противоположное флагу -W, то есть отключает все предупреждения.
Обратная совместимость
Если вы привыкли работать с версией Perl до появления лексически-ограниченных предупреждений или у вас есть код, использующий как лексические предупреждения, так и $^W, этот раздел опишет, как они взаимодействуют.
Взаимодействие лексических предупреждений с -w/$^W:
-
Если ни один из трёх флагов командной строки (-w, -W или -X), контролирующих предупреждения, не используется, и ни
$^W, ни прагмаwarningsне используются, то стандартные предупреждения будут включены, а необязательные предупреждения отключены. Это означает, что устаревший код, который не пытается управлять предупреждениями, будет работать без изменений. -
Флаг -w просто устанавливает глобальную переменную
$^Wкак в 5.005. Это означает, что любой устаревший код, который в настоящее время полагается на манипулирование$^Wдля управления поведением предупреждений, по-прежнему будет работать как есть. -
Помимо того, что теперь это булево значение, переменная
$^Wработает точно так же, как и ужасно неконтролируемым глобальным образом, за исключением того, что она не может отключать/включать стандартные предупреждения. -
Если фрагмент кода находится под управлением прагмы
warnings, то как переменная$^W, так и флаг -w будут игнорироваться в пределах области действия лексического предупреждения. -
Единственный способ переопределить настройку лексических предупреждений — это флаги командной строки -W или -X.
Совместный эффект пунктов 3 и 4 заключается в том, что это позволит коду, использующему прагму warnings, управлять поведением предупреждений типа $^W (используя local $^W=0) при необходимости, но не наоборот.
Иерархия категорий
Была определена иерархия «категорий», чтобы группы предупреждений можно было включать/отключать изолированно.
Текущая иерархия:
all -+
|
+- closure
|
+- deprecated
|
+- exiting
|
+- experimental --+
| |
| +- experimental::alpha_assertions
| |
| +- experimental::args_array_with_signatures
| |
| +- experimental::bitwise
| |
| +- experimental::builtin
| |
| +- experimental::const_attr
| |
| +- experimental::declared_refs
| |
| +- experimental::defer
| |
| +- experimental::extra_paired_delimiters
| |
| +- experimental::for_list
| |
| +- 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::try
| |
| +- experimental::uniprop_wildcards
| |
| +- experimental::vlb
|
+- glob
|
+- imprecision
|
+- io ------------+
| |
| +- closed
| |
| +- exec
| |
| +- layer
| |
| +- newline
| |
| +- pipe
| |
| +- syscalls
| |
| +- unopened
|
+- locale
|
+- misc
|
+- missing
|
+- numeric
|
+- once
|
+- overflow
|
+- pack
|
+- portable
|
+- recursion
|
+- redefine
|
+- redundant
|
+- regexp
|
+- scalar
|
+- 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 Как и прагма «strict», любые из этих категорий могут быть объединены.
use warnings qw(void redefine);
no warnings qw(io syntax untie); Также, как и прагма «strict», если в данном контексте присутствует более одной инстанции прагмы 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 приводит один пример подмножества предупреждений, которое, по мнению авторов модуля, относительно безопасно для критического предупреждения.
ПРИМЕЧАНИЕ: Пользователи предупреждений 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, поэтому программа завершается немедленно при обнаружении предупреждения.
Чтобы явно отключить предупреждение «FATAL», просто отключите предупреждение, с которым оно связано. Например, для отключения предупреждения «void» в примере выше, можно использовать следующие способы:
no warnings qw(void);
no warnings FATAL => qw(void); Если вы хотите понизить предупреждение, которое было повышено до ошибки FATAL, обратно до обычного предупреждения, вы можете использовать ключевое слово «NONFATAL». Например, приведенный ниже код повысит все предупреждения до уровня FATAL, за исключением тех, которые относятся к категории «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';" Однако следует придерживаться советов в начале этого раздела, касающихся избегания использования use warnings FATAL => 'all';.
Если вы хотите, чтобы ваша программа была совместима с версиями Perl до 5.20, вы должны использовать use warnings FATAL => 'all'; вместо этого. (В предыдущих версиях Perl поведение операторов use warnings 'FATAL';, use warnings 'NONFATAL'; и no warnings 'FATAL'; было не определено; они не вели себя так, как если бы они включали часть => 'all'. Начиная с версии 5.20, они это делают.)
Отправка предупреждений из модуля
Pragma warnings предоставляет ряд функций, полезных для авторов модулей. Они используются, когда необходимо сообщить модуль-специфическое предупреждение вызывающему модулю, который включил предупреждения с помощью pragmy 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, фактически включил их с помощью pragmy 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, поскольку они могут использовать функцию, которая позволяет повышать предупреждения до уровня ошибок FATAL. Таким образом, в этом случае
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 $x = Original->new();
$x->doit(1);
my $y = Derived->new();
$x->doit(1); При выполнении этого кода только объект Derived, $y, будет генерировать предупреждение.
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
-
Создаёт новую категорию предупреждений с тем же именем, что и пакет, в котором используется pragma.
- 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)
-
Это регистрирует категории предупреждений для заданных имён и предназначено в первую очередь для использования в pragma warnings::register.
См. также "Pragmatic Modules" in perlmodlib и perldiag.
© 1993–2021 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.36.0/warnings