Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

RFC: Acme::ExceptionEater

by kyle (Abbot)
on Sep 07, 2007 at 21:44 UTC ( [id://637764]=perlmeditation: print w/replies, xml ) Need Help??

Acme::ExceptionEater derives from this exchange I had with tye. I'm dismayed by how easy it is to foil my preferred method of error handling. The time I spent implementing this module to demonstrate foiling error handling was mostly spent on the POD and tests (which will accompany my eventual upload to CPAN). The actual code boils down to this:

package Acme::ExceptionEater; sub new { bless {}, shift } sub DESTROY { eval {} }

I'm hoping that a future version of Perl will break my module completely.

In the meantime, I'm hoping for thoughts and suggestions from my fellow monks.

package Acme::ExceptionEater; use strict; use warnings; use vars qw( $VERSION ); use version; $VERSION = qv('0.0.1'); # Module implementation here sub new { bless {}, shift } sub DESTROY { eval {} } 1; # Magic true value required at end of module __END__ =head1 NAME Acme::ExceptionEater - Prevents eval from returning an exception. =head1 VERSION This document describes Acme::ExceptionEater version 0.0.1 =head1 SYNOPSIS use Acme::ExceptionEater; eval { my $ee = Acme::ExceptionEater->new(); die 'My final wish is for you to know this...'; }; # $@ is still '' =head1 DESCRIPTION Placing an Acme::ExceptionEater object in a lexical in the outer-most scope of an C<eval> will prevent exceptions from escaping the C<eval> where they may confuse, annoy, frighten, or inform others. Simply instanciate an Acme::ExceptionEater object at the start of the eval. When the eater goes out of scope and Perl does garbage collection, it will eat any exceptions that might be waiting to pass on their final words to the code after the eval. =head1 METHODS =over 4 =item new Creates a new Acme::ExceptionEater object. For Acme::ExceptionEater t +o work, this object must not be prematurely destroyed. =back =head1 DIAGNOSTICS None. Acme::ExceptionEater produces fewer than zero error messages. =head1 CONFIGURATION AND ENVIRONMENT Acme::ExceptionEater requires no configuration files or environment va +riables. =head1 DEPENDENCIES None. The tests for this module use Readonly. =head1 INCOMPATIBILITIES None reported. =head1 BUGS AND LIMITATIONS No bugs have been reported. Please report any bugs or feature requests to C<bug-acme-exceptioneater@rt.cpan.org>, or through the web interface a +t L<http://rt.cpan.org>. =head1 CONTRIBUTORS Author: Kyle Hasselbacher C<< <kyleha@gmail.com> >> http://perlmonks.org/?node=kyle The idea for Acme::ExceptionEater came from an interaction with Tye McQueen, http://perlmonks.org/?node=tye at http://perlmonks.org/?node_id=637425 =head1 LICENCE AND COPYRIGHT Copyright (c) 2007, Kyle Hasselbacher C<< <kyleha@gmail.com> >>. All r +ights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L<perlartistic>.

A future version of Acme::ExceptionEater may implement randomly eating exceptions at an arbitrary probability. This will enhance its usefulness as a tool to annoy maintenance programmers.

Replies are listed 'Best First'.
Re: RFC: Acme::ExceptionEater
by TGI (Parson) on Sep 08, 2007 at 00:44 UTC

    Why not take arguments in your use statement, and then inject an exception eating DESTROY method into any listed classes. Just pervert the code tye posted in response to you, et viola!

    package Acme::ExceptionEater; my %done; sub import { shift @_; for my $pkg ( @_ ) { next if $done{$pkg}++; $pkg .= "::DESTROY"; my $orig= \&{$pkg}; no warnings; *{$pkg}= sub { $orig->(@_); eval{}; } } }

    Warning: This is a totally untested copy/paste/perversion of tye's code.

    Assuming I didn't break it, then you can do:

    use Happy::Friendly::Package; use Acme::ExceptionEater qw( Happy::Friendly::Package );

    And you too will have mysteriously silent failures in your code!


    TGI says moo

      Wow, TGI, I love that idea! I regret that I have but one vote to give for it. Thank you!

        I don't think my mother wants me to play with you guys.
      While you are at it, you could look at the symbol table and inject the exception-eating DESTROY into every package that happens to be loaded! ;-)

        I was thinking about that yesterday, but I couldn't figure out where to look to get the required info in a reliable fashion.

        The obvious place to look is in %INC. But that gives you munged names, and only on a per file included basis. Files that define multiple packages would not be handled properly.

        Based on my investigation of perlguts and perlapi each namespace has a stash associated with it. So if you have a list of all the stashes, you can get their names, and if you have their names, you have all the namespaces you want to add a perverse DESTROY method to.

        Do you have any idea where to find either a list of active namespaces or stashes?


        TGI says moo

      now we know what the phrase "evil hacker" really means... ;-]

      Don Wilde
      "There's more than one level to any answer."
Re: RFC: Acme::ExceptionEater
by TGI (Parson) on Sep 12, 2007 at 00:56 UTC

    This seems to do the automatic wrapping properly. It just needs some clean-up and modulification, and it should be ready to ruin the day of anyone foolish enough to use it.

    use strict; # Symbol table experiments. use Data::Dumper; #use Win32::SerialPort; # All entries in symbol table are uniquely identified by a typeglob. #print join "\n", # $::{'main::'}, # $main::{'main::'}, # $main::main::{'main::'}, # $main::main::main::{'main::'}, # ; my %OMIT; BEGIN { my @OMIT = qw( *main::UNIVERSAL:: ); @OMIT{@OMIT} = (); } wrap_destroy($_) foreach find_namespaces( 'main::' ); eval { my $f = Foo->new(); die 'adsfasdf'; }; eval { my $f = Foo::Foo->new(); die 'adsfasdf'; }; eval { my $f = Foo::Foo::Foo->new(); die 'adsfasdf'; }; eval { my $f = Foo::Foo::Bar->new(); die 'adsfasdf'; }; sub find_namespaces { my $seen = ref($_[0]) eq 'HASH' ? shift : {} ; my @names = @_; my @results; foreach my $name ( @names ) { no strict 'refs'; foreach my $entry ( keys %{$name} ) { next unless $entry =~ /::$/; my $typeglob = ${$name}{$entry}; next if $$seen{$typeglob}++; find_namespaces( $seen, $typeglob ); } } return keys %$seen ; } sub has_destroy { my $namespace = shift; no strict 'refs'; if ( exists ${$namespace}{DESTROY} ) { print "\t$namespace has DESTROY\n"; } } sub inherits_destroy { my $namespace = shift; $namespace =~ s/^\*(main::)?//; $namespace =~ s/::$//; return UNIVERSAL::can( $namespace, 'DESTROY' ); } sub wrap_destroy { my $namespace = shift; no warnings; no strict 'refs'; my $eater = $namespace . "DESTROY"; return if exists $OMIT{$namespace}; my $sub; if( has_destroy( $namespace ) ) { my $orig = \&{ ${$namespace}{DESTROY} }; $sub = sub { print "$eater Ate $namespace\n"; my @caller = caller(1); my @this = caller(1); $orig->(@_); eval{}; }; } elsif ( inherits_destroy( $namespace ) ) { print "$namespace inherits DESTROY\n";; } else { print "$namespace has no DESTROY\n";; $sub = sub { print "$eater Swallowed $namespace\n"; eval{}; } } *{ "$eater" }= $sub; } package Foo; sub new { bless {}, __PACKAGE__; } sub DESTROY { 1; } package Foo::Bar; sub new { bless {}, __PACKAGE__; } sub DESTROY { 1; } package Foo::Foo; sub new { bless {}, __PACKAGE__; } sub DESTROY { 1; } package Foo::Foo::Foo; sub new { bless {}, __PACKAGE__; } use base 'Foo::Foo'; package Foo::Foo::Bar; sub new { bless {}, __PACKAGE__; }


    TGI says moo

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://637764]
Approved by TStanley
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (7)
As of 2024-04-16 06:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found