croak/confess from within File::Find

P

Paul Lalli

I've been staring at this and playing with variations for over an hour
now. Can someone help me out? My goal is to be able to use croak()
from within a subroutine that is called by the &wanted subroutine
which is passed to File::Find::find(). I want the error message
printed as a result of this croak() to list the line number of the
call to the final subroutine. Here is a short-but-complete script to
demonstrate the problem I'm having:

#!/usr/bin/perl
use strict;
use warnings;
use Carp;
use File::Find;
die "Usage: $0 [croak|confess] [level]\n" unless @ARGV == 2;
my ($which, $carplevel) = @ARGV;

sub err {
$Carp::CarpLevel = $carplevel;
if ($which eq 'croak') {
croak "You did something bad!"; #line 12
} else {
confess "You did something bad!"; #line 14
}
}

sub wanted {
err(); #line 19
}

find(\&wanted, '.'); #line 22
__END__


So, find() is calling wanted() which is calling err() which is calling
croak(). What I want is an error message saying:
"You did something bad! at ff_carp.pl line 19". However:
$ ./ff_carp.pl croak 0
You did something bad! at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/Find.pm
line 810

Calling croak with a CarpLevel of 0 shows me where in File::Find the
wanted() subroutine is being called.

$ ./ff_carp.pl croak 1
You did something bad! at ./ff_carp.pl line 22

But if I call croak with a CarpLevel of 1, I instead get where in my
main file find() is being called.

So instead I tried looking at the whole stack trace, to see what's
going wrong. I tried calling confess() with a CarpLevel of 0:

$ ./ff_carp.pl confess 0
You did something bad! at ./ff_carp.pl line 14
main::err() called at ./ff_carp.pl line 19
main::wanted() called at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/
Find.pm line 810
File::Find::_find_dir('HASH(0x13e82c)', ., 2) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 690
File::Find::_find_opt('HASH(0x13e82c)', .) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 1193
File::Find::find('CODE(0x1b66d0)', .) called at ./ff_carp.pl
line 22

Now this is exactly what I'd expect. It's telling me the actual
confess was called on line 14, inside the err() function that was
called on line 19, inside the wanted() function that was called within
File::Find.pm. Exactly. And furthermore, if I skip a level by
setting CarpLevel to 1:

$ ./ff_carp.pl confess 1
You did something bad! at ./ff_carp.pl line 19
main::wanted() called at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/
Find.pm line 810
File::Find::_find_dir('HASH(0x13e82c)', ., 2) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 690
File::Find::_find_opt('HASH(0x13e82c)', .) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 1193
File::Find::find('CODE(0x1b66d0)', .) called at ./ff_carp.pl
line 22

Again, Exactly what I expect. I skipped a level, so the first level
being printed is the source of the err() call, line 19.

Now - how do I get croak() to behave the same way? I don't want to
print the entire stack trace, I just want it to tell me that the
subroutine was called from line 19, like confess() does when I skip a
level.

Thank you for any insight you can provide,
Paul Lalli
 
I

it_says_BALLS_on_your forehead

I've been staring at this and playing with variations for over an hour
now. Can someone help me out? My goal is to be able to use croak()
from within a subroutine that is called by the &wanted subroutine
which is passed to File::Find::find(). I want the error message
printed as a result of this croak() to list the line number of the
call to the final subroutine. Here is a short-but-complete script to
demonstrate the problem I'm having:

#!/usr/bin/perl
use strict;
use warnings;
use Carp;
use File::Find;
die "Usage: $0 [croak|confess] [level]\n" unless @ARGV == 2;
my ($which, $carplevel) = @ARGV;

sub err {
$Carp::CarpLevel = $carplevel;
if ($which eq 'croak') {
croak "You did something bad!"; #line 12
} else {
confess "You did something bad!"; #line 14
}

}

sub wanted {
err(); #line 19

}

find(\&wanted, '.'); #line 22
__END__

So, find() is calling wanted() which is calling err() which is calling
croak(). What I want is an error message saying:
"You did something bad! at ff_carp.pl line 19". However:
$ ./ff_carp.pl croak 0
You did something bad! at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/Find.pm
line 810

Calling croak with a CarpLevel of 0 shows me where in File::Find the
wanted() subroutine is being called.

$ ./ff_carp.pl croak 1
You did something bad! at ./ff_carp.pl line 22

But if I call croak with a CarpLevel of 1, I instead get where in my
main file find() is being called.

So instead I tried looking at the whole stack trace, to see what's
going wrong. I tried calling confess() with a CarpLevel of 0:

$ ./ff_carp.pl confess 0
You did something bad! at ./ff_carp.pl line 14
main::err() called at ./ff_carp.pl line 19
main::wanted() called at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/
Find.pm line 810
File::Find::_find_dir('HASH(0x13e82c)', ., 2) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 690
File::Find::_find_opt('HASH(0x13e82c)', .) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 1193
File::Find::find('CODE(0x1b66d0)', .) called at ./ff_carp.pl
line 22

Now this is exactly what I'd expect. It's telling me the actual
confess was called on line 14, inside the err() function that was
called on line 19, inside the wanted() function that was called within
File::Find.pm. Exactly. And furthermore, if I skip a level by
setting CarpLevel to 1:

$ ./ff_carp.pl confess 1
You did something bad! at ./ff_carp.pl line 19
main::wanted() called at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/
Find.pm line 810
File::Find::_find_dir('HASH(0x13e82c)', ., 2) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 690
File::Find::_find_opt('HASH(0x13e82c)', .) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 1193
File::Find::find('CODE(0x1b66d0)', .) called at ./ff_carp.pl
line 22

Again, Exactly what I expect. I skipped a level, so the first level
being printed is the source of the err() call, line 19.

Now - how do I get croak() to behave the same way? I don't want to
print the entire stack trace, I just want it to tell me that the
subroutine was called from line 19, like confess() does when I skip a
level.

Thank you for any insight you can provide,
Paul Lalli

I know this isn't much help (on the analysis side), but maybe by
pointing you to Carp::Heavy, I can do you some good.

http://search.cpan.org/src/NWCLARK/perl-5.8.8/lib/Carp/Heavy.pm

In particular, look at:
sub shortmess_heavy
sub short_error_loc
sub ret_summary
sub caller_info

hth!
 
A

anno4000

Paul Lalli said:
I've been staring at this and playing with variations for over an hour
now. Can someone help me out? My goal is to be able to use croak()
from within a subroutine that is called by the &wanted subroutine
which is passed to File::Find::find(). I want the error message
printed as a result of this croak() to list the line number of the
call to the final subroutine. Here is a short-but-complete script to
demonstrate the problem I'm having:

#!/usr/bin/perl
use strict;
use warnings;
use Carp;
use File::Find;
die "Usage: $0 [croak|confess] [level]\n" unless @ARGV == 2;
my ($which, $carplevel) = @ARGV;

sub err {
$Carp::CarpLevel = $carplevel;
if ($which eq 'croak') {
croak "You did something bad!"; #line 12
} else {
confess "You did something bad!"; #line 14
}
}

sub wanted {
err(); #line 19
}

Add this:

push our @CARP_NOT, 'File::Find';
find(\&wanted, '.'); #line 22
__END__

[snip]

Carp has a hard time assigning an error location in callback situations.
It climbs the stack, essentially watching for a change in the calling
package. When called from a callback it meets that change earlier than
intended (from your main to File::Find). The variable @CARP_NOT is
checked and a package change is ignored if one of the packages is on
the other's @CARP_NOT.

Anno
 
P

Paul Lalli

Add this:

push our @CARP_NOT, 'File::Find';
Carp has a hard time assigning an error location in callback situations.
It climbs the stack, essentially watching for a change in the calling
package. When called from a callback it meets that change earlier than
intended (from your main to File::Find). The variable @CARP_NOT is
checked and a package change is ignored if one of the packages is on
the other's @CARP_NOT.

Anno,

Thank you for the information. Annoyingly, it seems that if all the
packages are "trusted" (as per Carp's docs), then croak behaves
exactly like confess:

#!/opt2/perl/bin/perl
use strict;
use warnings;
use Carp;
use File::Find;

sub err {
croak ("You did something bad!"); #line 8
}

sub wanted {
err(); #line 12
}
push our @CARP_NOT, 'File::Find';
find(\&wanted, '.'); #line 15
__END__


$ ./ff_carp.pl
You did something bad! at ./ff_carp.pl line 8
main::err() called at ./ff_carp.pl line 12
main::wanted() called at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/
Find.pm line 810
File::Find::_find_dir('HASH(0x13e8d4)', ., 2) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 690
File::Find::_find_opt('HASH(0x13e8d4)', .) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 1193
File::Find::find('CODE(0x15e498)', .) called at ./ff_carp.pl
line 15


I suppose that's better than nothing. It just bugs me that there
doesn't seem to be a way to just have it print where the subroutine
was called from. I suppose I could fiddle with caller() to get
exactly what I want, but doesn't it seem like carp/croak should be
able to do this on its own?

Paul Lalli
 
A

anno4000

Paul Lalli said:
Anno,

Thank you for the information. Annoyingly, it seems that if all the
packages are "trusted" (as per Carp's docs), then croak behaves
exactly like confess:

#!/opt2/perl/bin/perl
use strict;
use warnings;
use Carp;
use File::Find;

sub err {
croak ("You did something bad!"); #line 8
}

sub wanted {
err(); #line 12
}
push our @CARP_NOT, 'File::Find';
find(\&wanted, '.'); #line 15
__END__


$ ./ff_carp.pl
You did something bad! at ./ff_carp.pl line 8
main::err() called at ./ff_carp.pl line 12
main::wanted() called at /opt2/Perl5_8_4/lib/perl5/5.8.4/File/
Find.pm line 810
File::Find::_find_dir('HASH(0x13e8d4)', ., 2) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 690
File::Find::_find_opt('HASH(0x13e8d4)', .) called at /opt2/
Perl5_8_4/lib/perl5/5.8.4/File/Find.pm line 1193
File::Find::find('CODE(0x15e498)', .) called at ./ff_carp.pl
line 15


I suppose that's better than nothing. It just bugs me that there
doesn't seem to be a way to just have it print where the subroutine
was called from. I suppose I could fiddle with caller() to get
exactly what I want, but doesn't it seem like carp/croak should be
able to do this on its own?

You can have that if you compile the wanted() function in a package
of its own:

{
package CarpCatcher;
use Carp;
sub wanted {
err(); #line 12
}

sub err {
croak ("You did something bad!");
}

push our @CARP_NOT, 'File::Find';
}

use File::Find;

find(\&CarpCatcher::wanted, '.');
__END__

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top