I've included below the smallest possible reproduction case. I'm using
perl 5.8.4 on Linux with Spreadsheet::WriteExcel 2.17:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel::Big;
eval {
my $workbook = Spreadsheet::WriteExcel::Big->new("/tmp/foo.$$");
die "ERROR SUCCESSFULLY PROPAGATED!";
};
if ($@) {
warn "an error occurred |$@|\n";
}
else {
warn "no error occurred\n";
}
# What I expect: an error occurred |ERROR SUCCESSFULLY PROPAGATED! at
except3.pl line 8.
# What I get: no error occurred
I saw your posting to the Error.pm RT as well but I didn't have a
chance to reply.
The reason that this is happening is that "my $workbook" is declared
within the scope of the eval block but is never used and thus gets
garbage collected. This in turn calls DESTROY on the the child objects.
Somewhere in there the code exits the eval without setting $@. I can't
trace deep enough into it to say why.
Anyway, if you use the workbook in your code it won't get garbage
collected and you will get the behaviour that you expect:
#!/usr/bin/perl -w
use strict;
use Spreadsheet::WriteExcel::Big;
my $workbook;
eval {
$workbook = Spreadsheet::WriteExcel::Big->new("/tmp/foo.$$");
die "ERROR SUCCESSFULLY PROPAGATED!";
};
if ($@) {
warn "an error occurred |$@|\n";
}
else {
warn "no error occurred\n";
}
my $worksheet = $workbook->add_worksheet();
__END__
John.
--
I dug a little further into this and I think I see what is happening.
The following is the sequence of events:
* The program enters the eval block.
* The new $workbook is created and scoped via my to the eval block.
* The die() is called and $@ is set to the specified string.
* The program exits the eval block.
* The ref count for $workbook drops to 0.
* The $workbook is garbage collected.
* The Workbook DESTROY() method is called to close the excel file.
* In one of the destructors an eval is used and $@ is reset to ''.
Probably not a very common situation but I'll fix it in the next
release.
John.
--
This is exactly correct.
The specific locations that clobber $@ during garbage-collection are as
follows:
1 - Format.pm lines 715 and 718 (two eval() statements, of which one
will be executed)
2 - "require Encode;" at line 493 of Workbook.pm
We're going to temporarily and internally solve this by doing the
following, until your next release is ready. While the below change
corrects the behavior, I'd very much welcome your feeling on the
approach. We're going to change in Workbook.pm:
sub DESTROY {
my $self = shift;
$self->close() if not $self->{_fileclosed};
}
to:
sub DESTROY {
my $self = shift;
my $dollar_at = $@ if $@;
$self->close() if not $self->{_fileclosed};
$@ = $dollar_at if $dollar_at;
}
> Probably not a very common situation but I'll fix it in the next
> release.
Thank you kindly, very much appreciated! Not common I agree, but when
it does occur, it can be extraordinarily nasty.
For what it is worth here are the patches as they will probably be
applied. Loading Encode at the start stops it from clobbering $@ in
DESTROY and local() takes care of the other issue.
--- ~/Prep217/lib/Spreadsheet/WriteExcel/Workbook.pm Sun May 21
01:17:00 2006 UTC
+++ ~/Prep218/lib/Spreadsheet/WriteExcel/Workbook.pm Sat Jan 6 01:18:52
2007 UTC
@@ -145,8 +145,11 @@
# Set colour palette.
$self->set_palette_xl97();
+ # Load Encode if we can.
+ require Encode if $] >= 5.008;
+
$self->_initialize();
return $self;
}
--- ~/Prep217/lib/Spreadsheet/WriteExcel/Format.pm Sun May 21
01:17:00 2006 UTC
+++ ~/Prep218/lib/Spreadsheet/WriteExcel/Format.pm Sat Jan 6 01:17:02
2007 UTC
@@ -710,8 +718,9 @@
# Evaling all $values as a strings gets around the problem of
some
# numerical format strings being evaluated as numbers, for
example
# "00000" for a zip code.
#
+ local $@;
if (defined $value) {
eval "/$self->set_$key('$value')";
}
else {
I'll leave it to you to close the Error.pm tracker. :-)
John.
--