предупреждения
СОДЕРЖАНИЕ
ИМЯ
warnings - Perl-прагма для управления необязательными предупреждениями
СИНОПСИС
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"); ОПИСАНИЕ
Прагма warnings предоставляет контроль над тем, какие предупреждения включены в каких частях программы Perl. Это более гибкая альтернатива как флагу командной строки -w, так и эквивалентной переменной Perl, $^W.
Эта прагма работает точно так же, как и прагма strict. Это означает, что область действия прагмы предупреждений ограничена содержащимся блоком. Это также означает, что настройки прагмы не будут передаваться через файлы (через 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; С появлением лексических предупреждений обязательные предупреждения теперь стали стандартными предупреждениями. Разница заключается в том, что, хотя ранее обязательные предупреждения по-прежнему включены по умолчанию, их можно впоследствии включить или отключить с помощью лексической прагмы предупреждений. Например, в коде ниже, предупреждение "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), второй вызов doit сработает на "Use of uninitialized value" предупреждение, в то время как первый — нет.
sub doit
{
my $b; chop $b;
}
doit();
{
local ($^W) = 1;
doit()
} Это побочный эффект динамического области действия $^W.
Лексические предупреждения устраняют эти ограничения, позволяя более точно контролировать, где могут или не могут быть вызваны предупреждения.
Управление предупреждениями из командной строки
Существует три флага командной строки, которые можно использовать для управления тем, когда генерируются предупреждения (или не генерируются):
- -w
-
Это существующий флаг. Если лексическая прагма предупреждений не используется ни в одном из ваших кодов или модулей, которые вы используете, этот флаг включит предупреждения везде. См. «Обратная совместимость» для получения подробностей о взаимодействии этого флага с лексическими предупреждениями.
- -W
-
Если флаг -W используется в командной строке, он включит все предупреждения в программе независимо от того, были ли предупреждения отключены локально с помощью
no warningsили$^W =0. Это включает все файлы, которые включаются черезuse,requireилиdo. Представьте его как аналог команды «lint» для Perl. - -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::bitwise
| |
| +- experimental::const_attr
| |
| +- experimental::declared_refs
| |
| +- experimental::lexical_subs
| |
| +- experimental::postderef
| |
| +- experimental::re_strict
| |
| +- experimental::refaliasing
| |
| +- experimental::regex_sets
| |
| +- experimental::script_run
| |
| +- experimental::signatures
| |
| +- experimental::smartmatch
| |
| +- 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 Как и прагма «строго», любая из этих категорий может быть объединена
use warnings qw(void redefine);
no warnings qw(io syntax untie); Точно так же, как и прагма «строго», если в данной области действия существует более одной экземпляра прагмы 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 => 'all', должны понимать, что они рискуют будущей переносимостью своих программ, делая это. Perl ни в коем случае не обязуется не вводить новые предупреждения или категории предупреждений в будущем; на самом деле, мы оставляем за собой право сделать это. Код, который сейчас может не генерировать предупреждения, может генерировать их в будущей версии Perl, если команда разработчиков Perl 5 посчитает это в наилучших интересах сообщества. Если код, использующий фатальные предупреждения, сломается из-за появления нового предупреждения, мы НЕ будем считать это несовместимым изменением. Пользователи фатальных предупреждений должны проявлять особую осторожность во время обновлений, чтобы проверить, вызывает ли их код какие-либо новые предупреждения, и должны уделять особое внимание мелким деталям документации используемых функций, чтобы убедиться, что они не используют функции, которые в документации обозначены как рискованные, устаревшие или не определенные, или где в документации сказано «так что не делайте этого», или что-то в этом же духе. Использование таких функций в сочетании с фатальными предупреждениями ВСЕЦЕЛО НА РИСКЕ ПОЛЬЗОВАТЕЛЯ.
В данном документе описано использование предупреждений FATAL, но разработчики Perl 5 настоятельно рекомендуют ознакомиться с рисками, особенно при написании кода библиотек, предназначенных для использования другими, так как у пользователей последующих уровней нет возможности изменить выбор категорий предупреждений.
В приведенном ниже коде использование 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.28.3/warnings