in reply to What is the best way to extend packages with global error handlers?

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.

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

Replies are listed 'Best First'.
Re^2: What is the best way to extend packages with global error handlers?
by ELISHEVA (Prior) on Feb 23, 2009 at 09:15 UTC
    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