
btilly at gmail
Oct 22, 2006, 2:07 PM
Post #2 of 7
(410 views)
Permalink
|
|
Re: Why aren't %Carp::Internal and %Carp::CarpInternal documented?
[In reply to]
|
|
On 10/22/06, Jos Boumans <kane[at]dwim.org> wrote: > > Glancing at the source code for a current copy of Carp, I see that Jos > > I. Boumans <kane[at]dwim.org> claimed that %Carp::CarpInternal and > > %Carp::Internal do not work as advertised and therefore removed the > > documentation. > > I did not remove documentation -- I've added documentation for the other > global variables, as none of them were documented. The comment reads: > > # Comments added by Jos I. Boumans <kane[at]dwim.org> 11-Aug-2004 > # I can not get %CarpInternal or %Internal to work as advertised, > # therefor 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 ;) Hrm, right. With that reminder I remember having been unhappy enough with how the previous internal interfaces were written that I didn't want to publically document the new internal interfaces. But if we're going to document internal interfaces, I think we should document the reliable ones. Yes, $CarpLevel may be around and in heavy use, but those uses generally don't really do what people think they do. In fact the documented example that was put in the Carp documentation doesn't do what the documentation claims it does! (A double irony, the original carp() function does what the documentation claims they want to do with that example.) > > That is false. I just double-checked, and they do > > what I said they did. Really. > > > > What putting a package in %Carp::Internal does is... > > I'm glad you managed to illustrate and test the behaviour as advertised. > Perhaps > you could convert this illustration to a patch to the documentation and > test suite of Carp? See the end of this message. I patched the documentation and the test suite to correct a number of things. Specifically I did the following: - Documented %Carp::Internal and %Carp::CarpInternal. - Rewrote the documentation of $Carp::CarpLevel. In so doing I removed the example that didn't do what it said it did, explained what it actually does do, and explained how people usually messed it up. I also pointed people at the alternatives that are available and are more likely to do the right thing. - Removed documentation of shortmess and longmess. Unfortunately longmess doesn't do anything particularly reasonable, and it can't be made to do so because of backwards compatibility with code that depended on the old behaviour. - Added an internal comment in the implementation of longmess explaining why it doesn't do what you'd think it should do. - Added tests to verify various documented aspects of Carp's behaviour, including the newly documented %Carp::Internal and %Carp::CarpInternal. - Removed a couple of comments from Carp/Heavy.pm. - Modified the implementation in Carp/Heavy.pm so that it was easier to document. The change is that carp/croak will not report on any line within a package in %Carp::CarpInternal. This makes no practical difference because they previously did not report on any calls TO a line within a package in %Carp::CarpInternal, and the functions within Carp only call package Carp. But it is easier to precisely document this behaviour than the old. Cheers, Ben --- Carp.pm.bak 2006-10-22 09:59:26.000000000 -0700 +++ Carp.pm 2006-10-22 11:28:55.000000000 -0700 @@ -60,10 +60,6 @@ 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 @@ 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<carp> +or C<croak> 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<Carp> works, by changing some global variables in the C<Carp> namespace. See the section on C<GLOBAL VARIABLES> 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<carp> and c<croak> 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 @@ =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 @@ =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<carp> or C<croak>.) + +=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 @@ =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<Carp>'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<bar>'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 @@ =head2 $Carp::Verbose -This variable makes C<Carp> use the C<longmess> function at all times. -This effectively means that all calls to C<carp> become C<cluck> and -all calls to C<croak> become C<confess>. +This variable makes C<carp> and C<cluck> generate stack backtraces +just like C<cluck> and C<confess>. This is how C<use Carp 'verbose'> +is implemented internally. + +Defaults to C<0>. + +=head2 %Carp::Internal + +This says what packages are internal to Perl. C<Carp> 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<carp> or C<croak>. There errors +will not be reported on any lines that are calling packages in +C<%Carp::CarpInternal>. + +For example C<Carp> itself is listed in C<%Carp::CarpInternal>. +Therefore the full stack backtrace from C<confess> will not start +inside of C<Carp>, and the short message from calling C<croak> is +not placed on the line where C<croak> was called. + +=head2 $Carp::CarpLevel + +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<Carp>'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<Carp> 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. -Note, this is analogous to using C<use Carp 'verbose'>. +Therefore it is best to avoid C<$Carp::CarpLevel>. Instead use +C<@CARP_NOT>, C<%Carp::Internal> and %Carp::CarpInternal>. Defaults to C<0>. --- Heavy.pm.bak 2006-10-22 10:07:04.000000000 -0700 +++ Heavy.pm 2006-10-22 14:06:36.000000000 -0700 @@ -15,10 +15,6 @@ # these are called, they require Carp::Heavy which installs the real # routines. -# Comments added by Andy Wardley <abw[at]kfs.org> 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 @@ # $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 <kane[at]dwim.org> 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 @@ 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 @@ 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); --- Carp.t.bak 2006-10-22 11:00:53.000000000 -0700 +++ Carp.t 2006-10-22 13:35:55.000000000 -0700 @@ -8,7 +8,7 @@ use Carp qw(carp cluck croak confess); -plan tests => 21; +plan tests => 36; ok 1; @@ -72,6 +72,87 @@ }; 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 @@ } } - { local $TODO = "VMS exit status semantics don't work this way" if $Is_VMS; @@ -173,3 +253,45 @@ 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 $@; +}
|