我如何有条件地使警告致命?(How do I conditionally make warnings fatal?)

我希望有致命的错误可以降级为警告(或相反,警告可以提升为致命错误),具体取决于用户的偏好。 目前,我正在使用die并覆盖它,如下所示:

my $force; BEGIN { no warnings 'once'; Getopt::Long::Configure('pass_through'); GetOptions('force', \$force); *CORE::GLOBAL::die = sub { warn @_ } if $force; Getopt::Long::Configure('no_pass_through'); } use My::Module; ... die "maybe"; ... exit(1) if $force; exit(0); # success!

我不喜欢这种方法,主要是因为有一些地方我仍然想死(例如缺少命令行参数)。 我宁愿改变(大多数) die实例来warn并做一个条件use warnings FATAL => 'all' 。 问题是use warnings是词法范围的,所以这是无效的:

if (!$force) { use warnings FATAL => 'all'; }

尝试使用postfix条件是语法错误:

use warnings FATAL => 'all' unless $force;

并且不能在表达式中使用:

$force or use warnings FATAL => 'all'; # syntax error

我可以想到各种令人反感的解决方法(定义我自己的警告/死亡,在if / else中复制我的主要脚本代码等)但我正在寻求一种优雅的解决方案,有条件地在当前词法范围中应用use warnings 。 (在父范围中应用它也会起作用,但可能需要黑魔法。)


1:实际上,我想在我的模块中添加use warnings::register并use warnings FATAL => 'My::Module' ,但这个区别对于这个问题来说并不重要。

I want to have fatal errors that can be demoted to warnings (or conversely, warnings that can be promoted to fatal errors) depending on the user's preference. At present, I'm using die and overriding it like this:

my $force; BEGIN { no warnings 'once'; Getopt::Long::Configure('pass_through'); GetOptions('force', \$force); *CORE::GLOBAL::die = sub { warn @_ } if $force; Getopt::Long::Configure('no_pass_through'); } use My::Module; ... die "maybe"; ... exit(1) if $force; exit(0); # success!

I don't like this approach, mostly because there are a few places where I still want to die (e.g. missing command-line arguments). I'd rather change (most) instances of die to warn and do a conditional use warnings FATAL => 'all'1. The problem is that use warnings is lexically scoped, so this is ineffective:

if (!$force) { use warnings FATAL => 'all'; }

Attempting to use a postfix conditional is a syntax error:

use warnings FATAL => 'all' unless $force;

and use can't be used in an expression:

$force or use warnings FATAL => 'all'; # syntax error

I can think of various distasteful work-arounds (Defining my own warn/die, duplicating my main script code in an if/else, etc.) but am seeking an elegant solution that conditionally applies use warnings in the current lexical scope. (Applying it in the parent scope would work as well but would likely require dark magic.)


1: Actually, I want to add use warnings::register to my modules and use warnings FATAL => 'My::Module' to my script, but the distinction is unimportant for the purposes of this question.

最满意答案

你可以尝试:

use if !$force, warnings => qw( FATAL all );

是的, if与Perl一起分发,则会调用一个模块,以使条件导入变得非常容易。

请注意,这是在编译时计算的 ,因此需要在编译时定义$force 。 (可能在BEGIN { ... }块内。)根据你问题中的代码示例,你似乎已经想到了那个部分。

或者,更多手册:

BEGIN { require warnings; warnings->import(qw/ FATAL all /) unless $force; }

一种完全不同的方法是使用信号处理程序$SIG{__WARN__}或$SIG{__DIE__}来决定是否在运行时死亡。

You could try:

use if !$force, warnings => qw( FATAL all );

Yes, there's a module called if that is distributed with Perl to make it really easy to do conditional imports.

Note that this is evaluated at compile time, so $force needs to be defined at compile time. (Probably within a BEGIN { ... } block.) Based on the code samples in your question, you seem to have figured that part out already though.

Alternatively, more manual:

BEGIN { require warnings; warnings->import(qw/ FATAL all /) unless $force; }

A completely different approach would be to use a signal handler $SIG{__WARN__} or $SIG{__DIE__} to decide to die or not at run-time.

更多推荐