ELISHEVA has asked for the wisdom of the Perl Monks concerning the following question:

I'm working with a CPAN package that has decided to make its error handling customizable via a global variable storing an error handling subroutine. I'm really not sure how to best to extend the package and would like your collective wisdom.

I need to override the default handling because the default version returns a localized error string. In my extension I need to make my error handling conditional on the type of error. I don't want to trap and parse locale specific strings to guess the type of error. Fortunately the underlying API has locale independent error constants I can use instead. However, I only have access to those constants if I provide my own error handler.

Now here is the dilemma: if I set the global variable for the error handler sub directly (no localizing), then I run the risk of clashing with other CPAN modules I might want to use that also set the global variable. I also risk clashing with any consumer code of my module that wants to set their own handler. They may throw an error object I don't expect or I may throw an error object they don't expect. Neither is a "good thing".

If I localize the variable that stores the error handler, my extension is insulated from clashes, but now I've violated the user's expectation that they can customize error handling any way they wish. I don't like changing documented features.

So what is the best way to handle this? - do I

Also, is there a more stable Perlish way to allow some measure of customization for error handling? I think all of the above choices are lousy and would rather not put someone in the position of having to choose amongst them for any module I write.

Many thanks in advance, beth

  • Comment on What is the best way to extend packages with global error handlers?

Replies are listed 'Best First'.
Re: What is the best way to extend packages with global error handlers?
by tilly (Archbishop) on Feb 22, 2009 at 17:35 UTC
    What does the call stack look like at the point where the error is thrown?

    If your code is above the package you want to extend and below calling code then I would suggest that you copy the global variable with the original handler into a new variable, then install your own handler. Your handler should handle the errors it knows how to handle, and then (if the error is not handled perfectly) should call the original error handler with the original arguments.

    Any collection of packages that all take this strategy should cooperate fairly smoothly. In effect you are manually doing the same thing as setting up a normal exception handler, and then rethrowing any exceptions that you choose not to handle.

      Thanks! I think that idea will work just fine, though I needed to think a bit to avoid implementing it via cut-n-paste. I like the idea because it should also, I think, cooperate with packages that don't take that strategy. (Counter examples, welcome of course - but see how I've implemented it first).

      Since I have no control over when the global variable will be set, I needed to do the copy of the original handler/installation of my handler immediately before each call to third party module subroutines. At first I thought that meant fairly repetitive hand-coding of wrapper subs for each sub that needed a custom handler. That seems to me a bit of a hack.

      Then it occurred to me that I can avoid the hand-coding simply by require'ing rather than use'ing the package. Then, instead of importing, I would play around with the symbol table a bit in the begin block, like this:

      1. require the 3rd party module
      2. for each sub X that uses the global error handler, manufacture a wrapper sub that
        1. saves the original handler
        2. manufactures a closure that calls a custom handler If the custom handler didn't handle the exception, then the closure would call the original handler
        3. sets the (localized) global handler to manufactured closure
        4. calls sub X
      3. assign the manufactured wrapper to a sub of the same name in the package namespace

      For those who might be interested in this approach (or in case tilly has some ideas of an even better way to implement this), I've added a code sample below, in three parts. The first part is a dummy 3rd party class with a global variable as a handler. The second part is a sample extension module implementing tilly interception suggestion using generated wrappers. And the third part is a short demo showing that both the custom and global handler indeed get called properly even when the global handler is redefined long after all modules have been compiled and loaded.

      Although this works, it isn't exactly a beginner Perl extension strategy. The implementation requires an understanding of localization, symbol tables, begin blocks, closures, and subroutine generators. Even as it solves my problem with global handlers as a customization design, it underscores why global variables as a customization strategy are not a good idea - unless, of course, I've missed an easier way to do this.

      An ideal customization design would be something that preserved documented behavior, allowed for stable code, was non-disruptive, involved minimal or no cut-n-paste and could be used safely by a programmer with a basic knowledge of Perl.

      All the same, this does solve my immediate problem, so

      Many, many thanks for pointing the way, beth

Re: What is the best way to extend packages with global error handlers?
by zwon (Abbot) on Feb 22, 2009 at 11:55 UTC

    Can you reveal a name of this CPAN package?

Re: What is the best way to extend packages with global error handlers?
by Bloodnok (Vicar) on Feb 22, 2009 at 22:18 UTC
    Why not implement a wrapper around the sub-package emulating the perl signal handling mechanism ?

    A user level that continues to overstate my experience :-))
Re: What is the best way to extend packages with global error handlers?
by eyepopslikeamosquito (Archbishop) on Feb 23, 2009 at 07:02 UTC

    Also, is there a more stable Perlish way to allow some measure of customization for error handling?
    In general, Perl Best Practices (Chapter 13, "Error Handling") recommends throwing exceptions rather than returning special values or setting flags and, in particular, endorses the CPAN Exception::Class module.

      Thank you for looking that up. Throwing (rather than returning exceptions) is definitely a good idea, however, it won't solve this particular problem or create a more stable customization interface.

      The default implementation of the global error handler does just that, throw an exception, politely, with croak no less. The problem I was struggling with isn't how to pass the exception (return value, throwing), but rather (a) how best to work with the way this particular module chose to support customization of the throwing behavior (b) how to support customization of hooks (error handlers or others) without creating the issues presented by a global handler.

      Best, beth

Re: What is the best way to extend packages with global error handlers?
by Anonymous Monk on Feb 22, 2009 at 12:39 UTC
    Use objects?

      Thank you for your contribution. I assume you are referring to the second part of my question - what to do to avoid these problems?

      Object oriented programming/design (OOP/D) may be part of the answer, but in and of itself, it isn't the answer. The module in question is implemented as a class and the handler variable in OO terminology is class data. Simply putting a veneer of OO over a poor design doesn't solve the design problems. In this case the OO interface was probably an after-thought. Based on the internal source code documentation it appears that the original implementation might have had a functional, rather than OO interface. My guess is that the global handler is a hold over from that earlier implementation, and not an intentional choice to make something class level data.

      But, in a general sense, I agree with you. Had the original author used an object method/object data for the customization rather than global handler (class data), the various trade-offs discusssed in my original post could have been avoided. Extensions wishing custom error handling would simply subclass the original module. The mechanism used to select method implementations for objects would have insured that the right handler got matched with the right extension subroutines. There would have been no need for a begin block+localization (see my reply to tilly) to do this manually and much less reason to be concerned about conflicts.

      However, even had a method been used, that isn't the end of the story. In the sample code for my reply to tilly, I arranged things so that at least two different custom handlers were involved. In an OO solution, that could translate into at least three different designs, and possibly more:

      • three overridable methods - a default handler method and two custom handler methods.
      • a default method handler and a customization hash - the customization hash would mapped subs to custom handlers so that the user would have full control over which methods got assigned which custom handlers
      • a combination of the first two - the customization methods would provide default behavior when the customization hash was missing an entry for a particular subroutine.

      The choice of which is best isn't something that can't be made in the abstract. The hash is more likely to be stable, but may be more complexity than end-users want. The customization methods may be easier for end-users, but the scope of the problem may ultimately outgrow that solution. The mixed approach requires richer testing and more implementation time.

      Best, beth

      Update: various clarifications