Re: Where did $@ go?
by Corion (Patriarch) on Mar 21, 2011 at 08:22 UTC
|
The hip way of doing this is to use Try::Tiny. Personally, I prefer the following approach, to make sure the code actually died:
my $ok = eval {
$mailer->send(...);
1;
};
if (! $ok) {
my $err = $@;
...
};
But even my approach does not explain to me why if( $@ ) would be true but "$@" resp. "$err" would be empty. | [reply] [d/l] [select] |
|
Tracing back through the calls, I found "Try::Tiny". It claims to localize $@. But if it does funny things, it means the caller must use Try::Tiny as well to get sensible results! Does every try-ing module create a different dependency on its users? That would be a nightmare! I guess it "restores" it after the non-existent catch!
Using Try::Tiny, I get the thrown object in the catch block.
Even data dumper shows, in my original call, $@ to be ''. But the code branch is executed, so it is true.
As for your example, the core docs say that if eval completes without error, then $@ is guaranteed to be a null string, so you can write eval{...}; warn if $@;. You should not need to ensure your block evaluates to true, by original design of the feature.
| [reply] [d/l] [select] |
|
As for your example, the core docs say that if eval completes without error, then $@ is guaranteed to be a null string
Even if that would be true, then
so you can write eval{...}; warn if $@;
doesn't follow.
For the second, you also need $@ to be true if the eval fails. Which doesn't need to be the case.
The problem is end-of-scope effects. eval { } is a block. Leaving the block, whether due to reaching the end, entering a return, or because the code dies, triggers end of scope effects. Which may cause more code to be executed (think DESTROY blocks). Which may clear, or set $@.
This is a known, but hard problem to solve (as one doesn't want to lose information carried in $@). There will be improvements in 5.14, IIRC.
| [reply] [d/l] |
|
|
|
|
Re: Where did $@ go?
by ELISHEVA (Prior) on Mar 21, 2011 at 13:50 UTC
|
an empty string for $@ that tests as true
The last time I saw such weirdness (empty string tests as true), my so called empty string contained a null character as its first character. To confirm this print out $@ with Devel::Peek::Dump (see Devel::Peek).
As for how that null got there? Anybody's guess. Exiting an eval block triggers the destructors for all data that is no longer needed. If there is an eval in one of those destructors, $@ can be easily reset. I didn't see any DESTROY subroutine in either Mail::Builder::Simple or Mail::Builder. However, Mail::Builder uses Moose which has plenty of magic and there are several other modules involved as well. Any one of them could be doing something that causes $@ to be reset.
My own preferred syntax for exception handling is
eval {
... my code here ...
# eval is guaranteed to return undef if it fails
# this ensures we only enter the do block (return false)
# if and only if we die
# Note: returns from eval NOT containing sub
return 1;
} or do {
# save $@ because it is fragile and easily reset
my $e=$@;
... my error handling code here ...
};
| [reply] [d/l] [select] |
Re: Where did $@ go?
by syphilis (Archbishop) on Mar 21, 2011 at 12:25 UTC
|
And I get, no matter how I look at it, an empty string for $@ that tests as true
The trueness just goes to prove that $@ is not an empty string ... and that further proves that you're not looking at (or at least seeing) $@ in the right way.
Of course, that doesn't answer your question, but maybe it highlights what you need to focus on. Good luck with it - an intriguing situation :-)
Cheers, Rob | [reply] |
Re: Where did $@ go?
by hpavc (Acolyte) on Mar 21, 2011 at 11:59 UTC
|
| [reply] |
|
The docs show an example of:
print ref $@; # "Exception::Died"
Indicating that it converts plain strings or whatever into objects. But I'm getting no string back from ref $@ at all, not "Exception::Died" or any other class name.
That is, I should still see something is in $@ and be able to figure out what to do with it using data dumper, ref, etc. | [reply] [d/l] [select] |
Re: Where did $@ go?
by ikegami (Patriarch) on Mar 21, 2011 at 15:37 UTC
|
Care to give the output of
use Devel::Peek;
Dump($@);
| [reply] [d/l] |
|
I commented out the try and put an eval back in, and after the call got:
SV = PV(0x187eb88) at 0x1880ce8
REFCNT = 1
FLAGS = (ROK)
RV = 0x55b67b0
SV = PVHV(0x55aa5f0) at 0x55b67b0
REFCNT = 1
FLAGS = (OBJECT,OVERLOAD,SHAREKEYS)
STASH = 0x49f1540 "Email::Sender::Failure"
ARRAY = 0x55dfed0 (0:3, 1:3, 2:2)
hash quality = 111.4%
KEYS = 7
FILL = 5
MAX = 7
RITER = -1
EITER = 0x0
Elt "stack_trace" HASH = 0x7848e70
SV = PVMG(0x56b3b30) at 0x56778c0
REFCNT = 1
FLAGS = (ROK)
IV = 0
NV = 0
RV = 0x56779f8
SV = PVHV(0x55aa708) at 0x56779f8
REFCNT = 1
FLAGS = (OBJECT,OVERLOAD,SHAREKEYS)
STASH = 0x567ad90 "Devel::StackTrace"
ARRAY = 0x55dbba0 (0:5, 1:3)
hash quality = 150.0%
KEYS = 3
FILL = 3
MAX = 7
RITER = -1
EITER = 0x0
Elt "index" HASH = 0x91501139
SV = NULL(0x0) at 0x56779e0
REFCNT = 1
FLAGS = ()
PV = 0x56779f8 ""
CUR = 0
LEN = 0
Elt "_recipients" HASH = 0x82243041
SV = RV(0x56b5af8) at 0x56b5ae8
REFCNT = 1
FLAGS = (ROK)
RV = 0x56b5a58
SV = PVAV(0x5675d40) at 0x56b5a58
REFCNT = 1
FLAGS = ()
ARRAY = 0x258b8a0
FILL = 0
MAX = 0
ARYLEN = 0x0
FLAGS = (REAL)
Elt No. 0
SV = PV(0x56ae658) at 0x56b5ab8
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x56a4ff0 "sales@LEDsbythefoot.com"\0
CUR = 23
LEN = 24
Elt "stack_trace_class" HASH = 0x850b24fb
SV = PV(0x527e468) at 0x5677638
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x55dd2f0 "Devel::StackTrace"\0
CUR = 17
LEN = 24
PV = 0x55b67b0 ""
CUR = 0
LEN = 0
I changed it to Dump($@) if $@; to make sure it was still testing as true, and then got a shorter dump:
SV = PV(0x187eb88) at 0x1880ce8
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x5592da0 ""\0
CUR = 0
LEN = 8
So... testing it as a bool will destroy it! Am I reading that right?
| [reply] [d/l] [select] |
|
I'm not sure what I was hoping to find out, but I can tell you this: Email::Sender::Failure overloads stringification with fallback, which means the object gets stringified on testing it as a bool. $@ appears to be getting clobbered, which could happen in the stringification code.
I'm pretty sure that only referencing $@ once will do the trick:
if (my $e = $@) {
... use $e here ...
}
And if not, this should definitely do the trick:
if (my $e = "$@") {
... use $e here ...
}
That should get the value the if was seeing.
(You can move the assignment out of the "if" if you prefer.)
| [reply] [d/l] [select] |
|
|
|