You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@lucy.apache.org by Marvin Humphrey <ma...@rectangular.com> on 2015/02/16 18:23:57 UTC

[lucy-dev] Exception handling

Lucifers,

For the Python bindings, all Clownfish objects inherit from Python's `object`,
which allows them to be used in a Python context without conversion or
wrappers.  It would be nice if Clownish::Err inherited from Python's
Exception, for similar reasons.

We don't need to use Python's existing exception classes from Clownfish -- and
it's not realistic to attempt a lowest-common-denominator exception class
hierarchy which reconciles the extensive facilities of Python, Ruby, Java, C#,
etc.  Instead, it would suffice to be able to use the Python host's error
handling facilities with Clownfish exceptions -- raise them, catch them, etc.

    # Catch all Clownfish errors.
    try:
        ...
    except clownfish.Err as e:
        ...

    # Catch all Lucy errors.
    try:
        lucy.IndexSearcher.open(index='/path/to/index')
    except lucy.Error as e:
        ...

    # Catch only locking errors from Lucy.
    try:
        lucy.Indexer.open(index='/path/to/index')
    except lucy.LockError as e:
        ...

This basic approach, which should work for all hosts and be safe to embrace as
"idiomatic Clownfish", is described in the Python tutorial on user-defined
exceptions:

    https://docs.python.org/3/tutorial/errors.html#tut-userexceptions

    When creating a module that can raise several distinct errors, a common
    practice is to create a base class for exceptions defined by that module,
    and subclass that to create specific exception classes for different error
    conditions...

Unfortunately, we run into a multiple inheritance problem in Python: Python's
`Exception` class has member variables, causing a clash at the offset where
the Clownfish `klass` member variable resides.

Perhaps we can solve this problem by making Clownfish's exceptions under
Python wrap an host object which implements an interface, similar what to Nick
proposed back in December:

    http://s.apache.org/h0K

Marvin Humphrey

Re: [lucy-dev] Exception handling

Posted by Nick Wellnhofer <we...@aevum.de>.
On 17/02/2015 01:51, Marvin Humphrey wrote:
> On Mon, Feb 16, 2015 at 11:55 AM, Nick Wellnhofer <we...@aevum.de> wrote:
>> Can't we simply convert a Clownfish exception to a Python exception in
>> cfish_Err_do_throw (and convert it back in cfish_Err_trap)?
>
> Yes, but the problem is that such a mechanism prevents catching specific
> classes of exceptions.  In my provisional Python bindings, all Python method
> wrappers run Clownfish code inside Err_trap, and if the Clownfish code throws
> an exception, it's stringified and used as the message inside a Python
> RuntimeError.  Catching all RuntimeErrors is too broad.

Hmm, what about adding a "type" or "host_class" field to Clownfish::Err and 
use it to convert to exception objects of different classes.

Nick

Re: [lucy-dev] Exception handling

Posted by Marvin Humphrey <ma...@rectangular.com>.
On Mon, Feb 16, 2015 at 11:55 AM, Nick Wellnhofer <we...@aevum.de> wrote:
> The Python tutorial also says:
>
>> Exceptions should typically be derived from the Exception class
>
> This sounds like it's possible to create exceptions that are not derived
> from the Exception class.

That used to be possible, but it was deprecated in Python 2.5-2.7 and removed
in Python 3.

    https://www.python.org/dev/peps/pep-0352/

>> Perhaps we can solve this problem by making Clownfish's exceptions under
>> Python wrap an host object which implements an interface, similar what to
>> Nick
>> proposed back in December:
>>
>>      http://s.apache.org/h0K
>
>
> I can't see how this would help. Most of what I proposed there wouldn't be
> needed for Python anyway.

I was hoping for something like this:

Define a Python exception class, instantiate it, and pass it into Clownfish:

    class MyErr(Exception):
        def __init__(self, mess):
            self.mess = mess
        def get_mess(self):
            return self.mess

    err = MyErr("oops")
    clownfish.Err.throw(err) # <--- wrap Python object

If Clownfish's Err type were to be defined as a Go-style interface with one
method required (`Get_Mess`), the I think your proposal applies and this code
would work.  The conversion code within the Python binding for
clownfish.Err.throw() would know that it needs to create a wrapper object.

However, this doesn't yet solve the problem of converting the other direction.
It allows Clownfish code to use a Python Exception as a Clownfish Err, but
doesn't make it possible for Python to use a Clownfish Err as a Python
Exception.

> Can't we simply convert a Clownfish exception to a Python exception in
> cfish_Err_do_throw (and convert it back in cfish_Err_trap)?

Yes, but the problem is that such a mechanism prevents catching specific
classes of exceptions.  In my provisional Python bindings, all Python method
wrappers run Clownfish code inside Err_trap, and if the Clownfish code throws
an exception, it's stringified and used as the message inside a Python
RuntimeError.  Catching all RuntimeErrors is too broad.

    try:
        indexer = lucy.Indexer.open(index='/path/to/index')
    except RuntimeError as e:
        ...

We could potentially introduce a class like ClownfishError to use instead of
RuntimeError, but that wouldn't make the host exception facilities available
for Clownfish exception types and it wouldn't allow fine-grained catching of
things like LockError.  You'd have to resort to string comparison:

    try:
        indexer = lucy.Indexer.open(index='/path/to/index')
    except ClownfishError as e:
        if len(e.args) > 0 and str(e.args[0]).find("LockErr") != -1:
            print("Caught LockErr!")
        ...

Marvin Humphrey

Re: [lucy-dev] Exception handling

Posted by Nick Wellnhofer <we...@aevum.de>.
On 16/02/2015 18:23, Marvin Humphrey wrote:
> This basic approach, which should work for all hosts and be safe to embrace as
> "idiomatic Clownfish", is described in the Python tutorial on user-defined
> exceptions:
>
>      https://docs.python.org/3/tutorial/errors.html#tut-userexceptions
>
>      When creating a module that can raise several distinct errors, a common
>      practice is to create a base class for exceptions defined by that module,
>      and subclass that to create specific exception classes for different error
>      conditions...
>
> Unfortunately, we run into a multiple inheritance problem in Python: Python's
> `Exception` class has member variables, causing a clash at the offset where
> the Clownfish `klass` member variable resides.

The Python tutorial also says:

 > Exceptions should typically be derived from the Exception class

This sounds like it's possible to create exceptions that are not derived from 
the Exception class.

> Perhaps we can solve this problem by making Clownfish's exceptions under
> Python wrap an host object which implements an interface, similar what to Nick
> proposed back in December:
>
>      http://s.apache.org/h0K

I can't see how this would help. Most of what I proposed there wouldn't be 
needed for Python anyway.

Can't we simply convert a Clownfish exception to a Python exception in 
cfish_Err_do_throw (and convert it back in cfish_Err_trap)?

Nick