File Coverage

blib/lib/Exception/Warning.pm
Criterion Covered Total %
statement 50 64 78.1
branch 15 22 68.1
condition 5 6 83.3
subroutine 8 9 88.8
pod n/a
total 78 101 77.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package Exception::Warning;
4              
5             =head1 NAME
6              
7             Exception::Warning - Convert simple warn into real exception object
8              
9             =head1 SYNOPSIS
10              
11             # Convert warn into exception and throw it immediately
12             use Exception::Warning '%SIG' => 'die';
13             eval { warn "Boom!"; };
14             print ref $@; # "Exception::Warning"
15             print $@->warning; # "Boom!"
16              
17             # Convert warn into exception without die
18             use Exception::Warning '%SIG' => 'warn', verbosity => 4;
19             warn "Boom!"; # dumps full stack trace
20              
21             # Can be used in local scope only
22             use Exception::Warning;
23             {
24             local $SIG{__WARN__} = \&Exception::Warning::__WARN__;
25             warn "Boom!"; # warn via exception
26             }
27             warn "Boom!"; # standard warn
28              
29             # Run Perl with verbose warnings
30             $ perl -MException::Warning=%SIG,warn,verbosity=>3 script.pl
31              
32             # Run Perl which dies on first warning
33             $ perl -MException::Warning=%SIG,die,verbosity=>3 script.pl
34              
35             # Run Perl which ignores any warnings
36             $ perl -MException::Warning=%SIG,warn,verbosity=>0 script.pl
37              
38             # Debugging with increased verbosity
39             $ perl -MException::Warning=:debug script.pl
40              
41             =head1 DESCRIPTION
42              
43             This class extends standard L and converts warning into
44             real exception object. The warning message is stored in I
45             attribute.
46              
47             =for readme stop
48              
49             =cut
50              
51 1     1   32689 use 5.006;
  1         3  
  1         41  
52              
53 1     1   5 use strict;
  1         3  
  1         37  
54 1     1   5 use warnings;
  1         8  
  1         112  
55              
56             our $VERSION = '0.0401';
57              
58              
59             =head1 INHERITANCE
60              
61             =over 2
62              
63             =item *
64              
65             extends L
66              
67             =back
68              
69             =cut
70              
71             # Extend Exception::Base class
72             BEGIN {
73              
74             =head1 CONSTANTS
75              
76             =over
77              
78             =item ATTRS : HashRef
79              
80             Declaration of class attributes as reference to hash.
81              
82             See L for details.
83              
84             =back
85              
86             =head1 ATTRIBUTES
87              
88             This class provides new attributes. See L for other
89             descriptions.
90              
91             =over
92              
93             =cut
94              
95 1     1   53 my %ATTRS = ();
96 1         2 my @ATTRS_RO = ();
97              
98             =item warning : Str {ro}
99              
100             Contains the message which is set by C<$SIG{__WARN__}> hook.
101              
102             =cut
103              
104 1         2 push @ATTRS_RO, 'warning';
105              
106             =item message : Str = "Unknown warning"
107              
108             Contains the message of the exception. This class overrides the default value
109             from L class.
110              
111             =cut
112              
113 1         2 $ATTRS{message} = 'Unknown warning';
114              
115             =item string_attributes : ArrayRef[Str] = ["message", "warning"]
116              
117             Meta-attribute contains the format of string representation of exception
118             object. This class overrides the default value from L
119             class.
120              
121             =cut
122              
123 1         3 $ATTRS{string_attributes} = [ 'message', 'warning' ];
124              
125             =item default_attribute : Str = "warning"
126              
127             Meta-attribute contains the name of the default attribute. This class
128             overrides the default value from L class.
129              
130             =back
131              
132             =cut
133              
134 1         3 $ATTRS{default_attribute} = 'warning';
135              
136 1     1   5 use Exception::Base 0.21;
  1         19  
  1         6  
137 1         8 Exception::Base->import(
138             'Exception::Warning' => {
139             has => { ro => \@ATTRS_RO },
140             %ATTRS,
141             },
142             '+ignore_package' => [ 'Carp' ],
143             );
144             };
145              
146              
147             ## no critic qw(RequireArgUnpacking)
148             ## no critic qw(RequireCarping)
149              
150             =head1 IMPORTS
151              
152             =over
153              
154             =item use Exception::Warning '%SIG';
155              
156             =item use Exception::Warning '%SIG' => 'warn';
157              
158             Changes C<$SIG{__WARN__}> hook to C.
159              
160             =item use Exception::Warning '%SIG' => 'die';
161              
162             Changes C<$SIG{__WARN__}> hook to C function.
163              
164             =item use Exception::Warning ':debug';
165              
166             Changes C<$SIG{__WARN__}> hook to C and sets
167             verbosity level to 4 (maximum).
168              
169             =cut
170              
171             sub import {
172 8     8   48764 my ($pkg, @args) = @_;
173              
174 8         14 my @params;
175              
176 8         34 while (defined $args[0]) {
177 9         19 my $name = shift @args;
178 9 50       34 if ($name eq ':debug') {
179 0         0 $name = '%SIG';
180 0         0 @args = ('warn', 'verbosity', 4, @args);
181             };
182 9 100       26 if ($name eq '%SIG') {
183 6         10 my $type = 'warn';
184 6 100 66     139 if (defined $args[0] and $args[0] =~ /^(die|warn)$/) {
185 3         5 $type = shift @args;
186             };
187             # Handle warn hook
188 6 100       17 if ($type eq 'warn') {
189             # is 'warn'
190             ## no critic qw(RequireLocalizedPunctuationVars)
191 4         32 $SIG{__WARN__} = \&__WARN__;
192             }
193             else {
194             # must be 'die'
195             ## no critic qw(RequireLocalizedPunctuationVars)
196 2         14 $SIG{__WARN__} = \&__DIE__;
197             };
198             }
199             else {
200             # Other parameters goes to SUPER::import
201 3         6 push @params, $name;
202 3 100 100     35 push @params, shift @args if defined $args[0] and ref $args[0] eq 'HASH';
203             };
204             };
205              
206 8 100       29 if (@params) {
207 3         24 return $pkg->SUPER::import(@params);
208             };
209              
210 5         4383 return 1;
211             };
212              
213              
214             =item no Exception::Warning '%SIG';
215              
216             Undefines C<$SIG{__DIE__}> hook.
217              
218             =back
219              
220             =cut
221              
222             sub unimport {
223 3     3   98 my $pkg = shift;
224              
225 3         13 while (my $name = shift @_) {
226 5 100       18 if ($name eq '%SIG') {
227             # Undef die hook
228             ## no critic qw(RequireLocalizedPunctuationVars)
229 3         14 $SIG{__WARN__} = '';
230             };
231             };
232              
233 3         6 return 1;
234             };
235              
236              
237             # Warning hook with die
238             sub __DIE__ {
239 5 50   5   10737 if (not ref $_[0]) {
240             # Do not recurse on Exception::Died & Exception::Warning
241 5 50       15 die $_[0] if $_[0] =~ /^Exception::(Died|Warning): /;
242              
243             # Simple warn: recover warning message
244 5         9 my $message = $_[0];
245 5         27 $message =~ s/\t\.\.\.caught at (?!.*\bat\b.*).* line \d+( thread \d+)?\.\n?$//s;
246 5         31 while ($message =~ s/\t\.\.\.propagated at (?!.*\bat\b.*).* line \d+( thread \d+)?\.\n$//s) { };
247 5         23 $message =~ s/( at (?!.*\bat\b.*).* line \d+( thread \d+)?\.)?\n$//s;
248              
249 5         23 my $e = __PACKAGE__->new;
250 5         3619 $e->{warning} = $message;
251 5         32 die $e;
252             }
253             # Otherwise: throw unchanged exception
254 0           die $_[0];
255             };
256              
257              
258             # Warning hook with warn
259             sub __WARN__ {
260 0 0   0     if (not ref $_[0]) {
261             # Some optimalization
262 0 0         return if __PACKAGE__->ATTRS->{verbosity}->{default} == 0;
263              
264             # Simple warn: recover warning message
265 0           my $message = $_[0];
266 0           $message =~ s/\t\.\.\.caught at (?!.*\bat\b.*).* line \d+( thread \d+)?\.$//s;
267 0           while ($message =~ s/\t\.\.\.propagated at (?!.*\bat\b.*).* line \d+( thread \d+)?\.\n$//s) { };
268 0           $message =~ s/( at (?!.*\bat\b.*).* line \d+( thread \d+)?\.)?\n$//s;
269              
270 0           my $e = __PACKAGE__->new;
271 0           $e->{warning} = $message;
272 0           warn $e;
273             }
274             else {
275             # Otherwise: throw unchanged exception
276 0           warn $_[0];
277             };
278 0           return;
279             };
280              
281              
282             1;
283              
284              
285             =begin umlwiki
286              
287             = Class Diagram =
288              
289             [ <>
290             Exception::Warning
291             --------------------------------------------------------------
292             +message : Str = "Unknown warning"
293             +warning : Str {ro}
294             #default_attribute : Str = "warning"
295             #string_attributes : ArrayRef[Str] = ["message", "warning"]
296             --------------------------------------------------------------
297             <> -__DIE__()
298             <> -__WARN__()
299             <> +ATTRS() : HashRef ]
300              
301             [Exception::Warning] ---|> [Exception::Base]
302              
303             =end umlwiki
304              
305             =head1 PERFORMANCE
306              
307             The C module can change C<$SIG{__WARN__}> hook. It costs
308             a speed for simple warn operation. It was tested against unhooked warn.
309              
310             -------------------------------------------------------
311             | Module | run/s |
312             -------------------------------------------------------
313             | undef $SIG{__WARN__} | 276243/s |
314             -------------------------------------------------------
315             | $SIG{__WARN__} = sub { } | 188215/s |
316             -------------------------------------------------------
317             | Exception::Warning '%SIG' | 1997/s |
318             -------------------------------------------------------
319             | Exception::Warning '%SIG', verb.=>0 | 26934/s |
320             -------------------------------------------------------
321              
322             It means that C is significally slower than simple warn.
323             It is usually used only for debugging purposes, so it shouldn't be an
324             important problem.
325              
326             =head1 SEE ALSO
327              
328             L.
329              
330             =head1 BUGS
331              
332             If you find the bug or want to implement new features, please report it at
333             L
334              
335             =for readme continue
336              
337             =head1 AUTHOR
338              
339             Piotr Roszatycki
340              
341             =head1 LICENSE
342              
343             Copyright (C) 2008, 2009 by Piotr Roszatycki .
344              
345             This program is free software; you can redistribute it and/or modify it
346             under the same terms as Perl itself.
347              
348             See L