Skip to Content.
Sympa Menu

devel - Re: [sympa-developpers] Using exception

Subject: Developers of Sympa

List archive

Chronological Thread  
  • From: IKEDA Soji <address@concealed>
  • To: address@concealed
  • Subject: Re: [sympa-developpers] Using exception
  • Date: Mon, 14 Oct 2013 17:16:27 +0900

Guillaume and all,

To handle OO-based exceptions, we may have to prepare some sort of
module.

To make it real, I would like to try to follow some recommendations
by Conway. --- Please read the first section of Chapter 17.

At first, we have to "play test" interface, the author says. So I
have been requesting comments on my conding rule.
At the worst, if it is in any ways hard to read, write or
comprehend, we should throw away that idea and seek another
solution.

Please see also Chapter 13 on error and exception.

*

Previous coding rule of mine seems to have several problems:

- $EVAL_ERROR ($@) may be "contaminated": When processing has
exited from "catch" block, this global variable can have nonsense
value (see also the last section of Chapter 13).

Solution: A funtion, say, catch() to clear $@ is desirable.

- Mixture of OO exceptions and simple (string) exceptions: lower
levels of code can simply call die() or croak() with string
argument. As a result, tests by blessed() or so is necessary
on "catch" block. This worsen readability, and requires
"use Scalar::Util" line in all sources.

Solution: Simple exception should be converted to OO exception
automatically.
Example: See L<Exception::Class::Base/OVERLOADING>).

- Even if definitely unrecoverable error occurred, exception can be
intercept.

Solution: Define a "fatal" exception class that can never be caught.
Example: See L<Exception::Class/"Uncatchable exceptions">.

- Built-in function cannot throw exceptions: We have to surround
all built-in function calls with if-block checking their values.
This will worsen readability and productivity.

Solution: Use L<autodie> (formerly L<Fatal>).

- Use of $@ without dying: Several module functions such as
MIME::EncWords::decode_mime_words() sets $@ without dying
(N.B. this feature is not my original but derived from MIME::Words).

Soution: There seems nothing to do by our module.

- No easy way to define exception hierarchy. Everytime we need a
new exception class, we have to write new package.

Solution: Implement handy way to define new exception class.
Example: L<Exception::Class/"DECLARING EXCEPTION CLASSES">.

The first and the last problems force us modify coding rule. Others
are requiremnts our module must meet. So I compile both to one POD.

If there are no objections to anything in this POD, the next work is
to write test cases, not the code, the author says.


Regards,

--- Soji

--
株式会社 コンバージョン セキュリティ&OSSソリューション部 池田荘児
〒231-0004 神奈川県横浜市中区元浜町3-21-2 ヘリオス関内ビル7F
e-mail address@concealed TEL 045-640-3550
http://www.conversion.co.jp/

=encoding us-ascii

=head1 NAME

Sympa::Exception - Generating and processing exceptions

=head1 SYNOPSIS

Defining exceptions:

    use Sympa::Exception
        'Sympa::Exception::Foo' => { description => 'Foo occurred' },
        'Sympa::Exception::Bar' => { description => 'Bar emerged' },
        ;

Throwing an exception:

    Sympa::Exception::Foo->throw;

Catching exceptions:

    eval {
        ...
        # Any code possibly throws exceptions
        ...
    };
    if (my $e = Sympa::Exception->catch) {
        if ($e->isa('Sympa::Exception::Foo')) {
            ...
        } elsif ($e->isa('Sympa::Exception::Bar')) {
            ...
        } else {
            $e->throw;
        }
    }

=head1 DESCRIPTION

L<Sympa::Exception> provides feature to generate and process exceptions.

Exceptions are captured by C<eval { }> block.
catch() method consumes them.

Uncaught exceptions must be re-thrown.

=head2 Class method

=over

=item Sympa::Exception->catch

I<Class method>.
Consumes exception, if it had been thrown.
Returns caught exception object or false value.

If exception does not belong to the class descended from
L<Sympa::Exception::Base>, uncatchable exception (see below) will be thrown.

=back

=head2 Class methods of exception objects

=over

=item E<quot>E<quot>

I<Overridden operator>.
Returns error message and traceback.

By this function, exceptions never caught will output traceback to STDERR.

=item Sympa::Exception::I<Subclass>->isa ( CLASS_NAME )

I<Class method> of exception class.
Tests type of exception.

=item Sympa::Exception::I<Subclass>->throw ( ... )

I<Class method> of exception class.
Throws an exception.
This is equivalent to C<die Sympa::Exception::I<Subclass>-E<gt>new(...)>.

Note that the exceptions you don't wish to catch I<must> be thrown again.

=back

=head2 Basic exception classes

=over

=item Sympa::Exception::Base

Base class of all exceptions on Sympa.
This class I<should not> be used directly;
define appropriate subclasses and use them (see below).

=item Sympa::Exception::BuiltIn

The exception thrown by built-in functions.
To find out how to use this exception in details,
see L<Sympa::Exception::AutoDie>.

=item L<Sympa::Exception::Fatal>

Uncatchable exception.
This may be thrown when any programs of Sympa must terminate immediately.

=item L<Sympa::Exception::Internal>

The exception thrown from internals of Perl or external modules
by die(), croak() and so on with simple scalar argument.

=back

=head2 How to define new exceptions

There is an easy way to define arbitorary subclasses of
L<Sympa::Exception::Base>:

  use Sympa::Exception
      'Sympa::Exception::Foo'      => { description => 'Foo occurred' },
      'Sympa::Exception::Foo::Bar' => { description => 'Bar emerged',
                                        isa => 'Sympa::Exception::Foo' },
      ...
      ;

You may also define subclass by orthodox manner:

  package Sympa::Exception::Foo;
  use strict;
  use warnings;
  use base qw(Sympa::Exception::Base);
  ...
 
Anyway, it is I<recommended> that the names of new exception classes have
the prefix C<Sympa::>, for example C<Sympa::Exception::List>,
C<Sympa::List::Exception>.

=head1 BUGS

Some modules (e.g. L<MIME::EncWords>, L<Text::Balanced>) may set
C<$EVAL_ERROR> (C<$@>) without throwing exception so that catch() method
will be complicated.
To avoid such situation, localize C<$@> as below:

  my $decoded = do {
      local $@;
      MIME::EncWords::decode_mimewords($broken_header_line);
  };

=head1 SEE ALSO

L<Sympa::Exception::AutoDie>.

=head1 AUTHORS

Sympa developers and contributors.

=cut



Archive powered by MHonArc 2.6.19+.

Top of Page