From: Ben Tilly Date: Sun, 22 Oct 2006 14:07:23 +0000 (-0700) Subject: Re: Why aren't %Carp::Internal and %Carp::CarpInternal documented? X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=d735c2efe0b08b05adfb893625476bf4480a2ece;p=p5sagit%2Fp5-mst-13.2.git Re: Why aren't %Carp::Internal and %Carp::CarpInternal documented? From: "Ben Tilly" Message-ID: p4raw-id: //depot/perl@29270 --- diff --git a/lib/Carp.pm b/lib/Carp.pm index 5545c39..15e39e5 100644 --- a/lib/Carp.pm +++ b/lib/Carp.pm @@ -1,6 +1,6 @@ package Carp; -our $VERSION = '1.05'; +our $VERSION = '1.06'; # this file is an utra-lightweight stub. The first time a function is # called, Carp::Heavy is loaded, and the real short/longmessmess_jmp # subs are installed @@ -60,10 +60,6 @@ croak - die of errors (from perspective of caller) confess - die of errors with stack backtrace -shortmess - return the message that carp and croak produce - -longmess - return the message that cluck and confess produce - =head1 SYNOPSIS use Carp; @@ -72,30 +68,27 @@ longmess - return the message that cluck and confess produce use Carp qw(cluck); cluck "This is how we got here!"; - print FH Carp::shortmess("This will have caller's details added"); - print FH Carp::longmess("This will have stack backtrace added"); - =head1 DESCRIPTION The Carp routines are useful in your own modules because they act like die() or warn(), but with a message which is more likely to be useful to a user of your module. In the case of cluck, confess, and longmess that context is a summary of every -call in the call-stack. For a shorter message you can use carp, -croak or shortmess which report the error as being from where -your module was called. There is no guarantee that that is where -the error was, but it is a good educated guess. +call in the call-stack. For a shorter message you can use C +or C which report the error as being from where your module +was called. There is no guarantee that that is where the error +was, but it is a good educated guess. You can also alter the way the output and logic of C works, by changing some global variables in the C namespace. See the section on C below. -Here is a more complete description of how shortmess works. What -it does is search the call-stack for a function call stack where -it hasn't been told that there shouldn't be an error. If every -call is marked safe, it then gives up and gives a full stack -backtrace instead. In other words it presumes that the first likely -looking potential suspect is guilty. Its rules for telling whether +Here is a more complete description of how c and c work. +What they do is search the call-stack for a function call stack where +they have not been told that there shouldn't be an error. If every +call is marked safe, they give up and give a full stack backtrace +instead. In other words they presume that the first likely looking +potential suspect is guilty. Their rules for telling whether a call shouldn't generate errors work as follows: =over 4 @@ -107,15 +100,15 @@ Any call from a package to itself is safe. =item 2. Packages claim that there won't be errors on calls to or from -packages explicitly marked as safe by inclusion in @CARP_NOT, or -(if that array is empty) @ISA. The ability to override what +packages explicitly marked as safe by inclusion in C<@CARP_NOT>, or +(if that array is empty) C<@ISA>. The ability to override what @ISA says is new in 5.8. =item 3. The trust in item 2 is transitive. If A trusts B, and B -trusts C, then A trusts C. So if you do not override @ISA -with @CARP_NOT, then this trust relationship is identical to, +trusts C, then A trusts C. So if you do not override C<@ISA> +with C<@CARP_NOT>, then this trust relationship is identical to, "inherits from". =item 4. @@ -126,8 +119,15 @@ this practice is discouraged.) =item 5. -Any call to Carp is safe. (This rule is what keeps it from -reporting the error where you call carp/croak/shortmess.) +Any call to Perl's warning system (eg Carp itself) is safe. +(This rule is what keeps it from reporting the error at the +point where you call C or C.) + +=item 6. + +C<$Carp::CarpLevel> can be set to skip a fixed number of additional +call levels. Using this is not recommended because it is very +difficult to get it to behave correctly. =back @@ -151,21 +151,6 @@ See the C section below. =head1 GLOBAL VARIABLES -=head2 $Carp::CarpLevel - -This variable determines how many call frames are to be skipped when -reporting where an error occurred on a call to one of C's -functions. For example: - - $Carp::CarpLevel = 1; - sub bar { .... or _error('Wrong input') } - sub _error { Carp::carp(@_) } - -This would make Carp report the error as coming from C's caller, -rather than from C<_error>'s caller, as it normally would. - -Defaults to C<0>. - =head2 $Carp::MaxEvalLen This variable determines how many characters of a string-eval are to @@ -190,11 +175,57 @@ Defaults to C<8>. =head2 $Carp::Verbose -This variable makes C use the C function at all times. -This effectively means that all calls to C become C and -all calls to C become C. +This variable makes C and C generate stack backtraces +just like C and C. This is how C +is implemented internally. + +Defaults to C<0>. + +=head2 %Carp::Internal + +This says what packages are internal to Perl. C will never +report an error as being from a line in a package that is internal to +Perl. For example: + + $Carp::Internal{ __PACKAGE__ }++; + # time passes... + sub foo { ... or confess("whatever") }; + +would give a full stack backtrace starting from the first caller +outside of __PACKAGE__. (Unless that package was also internal to +Perl.) + +=head2 %Carp::CarpInternal + +This says which packages are internal to Perl's warning system. For +generating a full stack backtrace this is the same as being internal +to Perl, the stack backtrace will not start inside packages that are +listed in C<%Carp::CarpInternal>. But it is slightly different for +the summary message generated by C or C. There errors +will not be reported on any lines that are calling packages in +C<%Carp::CarpInternal>. + +For example C itself is listed in C<%Carp::CarpInternal>. +Therefore the full stack backtrace from C will not start +inside of C, and the short message from calling C is +not placed on the line where C was called. + +=head2 $Carp::CarpLevel -Note, this is analogous to using C. +This variable determines how many additional call frames are to be +skipped that would not otherwise be when reporting where an error +occurred on a call to one of C's functions. It is fairly easy +to count these call frames on calls that generate a full stack +backtrace. However it is much harder to do this accounting for calls +that generate a short message. Usually people skip too many call +frames. If they are lucky they skip enough that C goes all of +the way through the call stack, realizes that something is wrong, and +then generates a full stack backtrace. If they are unlucky then the +error is reported from somewhere misleading very high in the call +stack. + +Therefore it is best to avoid C<$Carp::CarpLevel>. Instead use +C<@CARP_NOT>, C<%Carp::Internal> and %Carp::CarpInternal>. Defaults to C<0>. diff --git a/lib/Carp.t b/lib/Carp.t index 2ce5eb4..63e1565 100644 --- a/lib/Carp.t +++ b/lib/Carp.t @@ -8,7 +8,7 @@ my $Is_VMS = $^O eq 'VMS'; use Carp qw(carp cluck croak confess); -plan tests => 21; +plan tests => 36; ok 1; @@ -72,6 +72,87 @@ eval { }; ok !$warning, q/'...::CARP_NOT used only once' warning from Carp::Heavy/; +# Test the location of error messages. +like(A::short(), qr/^Error at C/, "Short messages skip carped package"); + +{ + local @C::ISA = "D"; + like(A::short(), qr/^Error at B/, "Short messages skip inheritance"); +} + +{ + local @D::ISA = "C"; + like(A::short(), qr/^Error at B/, "Short messages skip inheritance"); +} + +{ + local @D::ISA = "B"; + local @B::ISA = "C"; + like(A::short(), qr/^Error at A/, "Inheritance is transitive"); +} + +{ + local @B::ISA = "D"; + local @C::ISA = "B"; + like(A::short(), qr/^Error at A/, "Inheritance is transitive"); +} + +{ + local @C::CARP_NOT = "D"; + like(A::short(), qr/^Error at B/, "Short messages see \@CARP_NOT"); +} + +{ + local @D::CARP_NOT = "C"; + like(A::short(), qr/^Error at B/, "Short messages see \@CARP_NOT"); +} + +{ + local @D::CARP_NOT = "B"; + local @B::CARP_NOT = "C"; + like(A::short(), qr/^Error at A/, "\@CARP_NOT is transitive"); +} + +{ + local @B::CARP_NOT = "D"; + local @C::CARP_NOT = "B"; + like(A::short(), qr/^Error at A/, "\@CARP_NOT is transitive"); +} + +{ + local @D::ISA = "C"; + local @D::CARP_NOT = "B"; + like(A::short(), qr/^Error at C/, "\@CARP_NOT overrides inheritance"); +} + +{ + local @D::ISA = "B"; + local @D::CARP_NOT = "C"; + like(A::short(), qr/^Error at B/, "\@CARP_NOT overrides inheritance"); +} + +# %Carp::Internal +{ + local $Carp::Internal{C} = 1; + like(A::short(), qr/^Error at B/, "Short doesn't report Internal"); +} + +{ + local $Carp::Internal{D} = 1; + like(A::long(), qr/^Error at C/, "Long doesn't report Internal"); +} + +# %Carp::CarpInternal +{ + local $Carp::CarpInternal{D} = 1; + like(A::short(), qr/^Error at B/ + , "Short doesn't report calls to CarpInternal"); +} + +{ + local $Carp::CarpInternal{D} = 1; + like(A::long(), qr/^Error at C/, "Long doesn't report CarpInternal"); +} # tests for global variables sub x { carp @_ } @@ -158,7 +239,6 @@ sub w { cluck @_ } } } - { local $TODO = "VMS exit status semantics don't work this way" if $Is_VMS; @@ -173,3 +253,45 @@ sub w { cluck @_ } is($?>>8, 42, 'confess() doesn\'t clobber $!'); } + +# line 1 "A" +package A; +sub short { + B::short(); +} + +sub long { + B::long(); +} + +# line 1 "B" +package B; +sub short { + C::short(); +} + +sub long { + C::long(); +} + +# line 1 "C" +package C; +sub short { + D::short(); +} + +sub long { + D::long(); +} + +# line 1 "D" +package D; +sub short { + eval{ Carp::croak("Error") }; + return $@; +} + +sub long { + eval{ Carp::confess("Error") }; + return $@; +} diff --git a/lib/Carp/Heavy.pm b/lib/Carp/Heavy.pm index f86b7b4..4355584 100644 --- a/lib/Carp/Heavy.pm +++ b/lib/Carp/Heavy.pm @@ -15,10 +15,6 @@ use Carp; our $VERSION = $Carp::VERSION; # these are called, they require Carp::Heavy which installs the real # routines. -# Comments added by Andy Wardley 09-Apr-98, based on an -# _almost_ complete understanding of the package. Corrections and -# comments are welcome. - # The members of %Internal are packages that are internal to perl. # Carp will not report errors from within these packages if it # can. The members of %CarpInternal are internal to Perl's warning @@ -28,12 +24,6 @@ use Carp; our $VERSION = $Carp::VERSION; # $Max(EvalLen|(Arg(Len|Nums)) variables are used to specify how the eval # text and function arguments should be formatted when printed. -# Comments added by Jos I. Boumans 11-Aug-2004 -# I can not get %CarpInternal or %Internal to work as advertised, -# therefore leaving it out of the below documentation. -# $CarpLevel may be decprecated according to the last comment, but -# after 6 years, it's still around and in heavy use ;) - # disable these by default, so they can live w/o require Carp $CarpInternal{Carp}++; $CarpInternal{warnings}++; @@ -48,6 +38,11 @@ our ($CarpLevel, $MaxArgNums, $MaxEvalLen, $MaxArgLen, $Verbose); sub longmess_real { # Icky backwards compatibility wrapper. :-( + # + # The story is that the original implementation hard-coded the + # number of call levels to go back, so calls to longmess were off + # by one. Other code began calling longmess and expecting this + # behaviour, so the replacement has to emulate that behaviour. my $call_pack = caller(); if ($Internal{$call_pack} or $CarpInternal{$call_pack}) { return longmess_heavy(@_); @@ -234,6 +229,7 @@ sub short_error_loc { return 0 unless defined($caller); # What happened? redo if $Internal{$caller}; + redo if $CarpInternal{$caller}; redo if $CarpInternal{$called}; redo if trusts($called, $caller, $cache); redo if trusts($caller, $called, $cache);