What c.l.py's opinions about Soft Exception?

Diez B. Roggisch deets at nospam.web.de
Tue Mar 11 13:00:50 EDT 2008


> If you passed an object that has several methods to call (using tuple
> or list) and you want to handle several softexceptions and ignore some
> others, you must still pass an empty methods to the one you want to
> ignore, cluttering the caller's code by significant degree:
> 
>     def somefunc(a, b, callback = (DO_NOTHING, DO_NOTHING, DO_NOTHING,
> DO_NOTHING)):
>         if a == 0: raise callback(0)
>         try:
>             a += b
>         except ZeroDivisionError:
>             raise callback(1)
>         if a <= 0: raise callback(2)
>         raise callback(3)
>         return a
> 
>     somefunc(a, b, (callbackzero, DO_NOTHING, callbacktwo,
> DO_NOTHING))

You misunderstood. I'd pass something like a context-object, which wold look
like this:

def somefunc(a, b, context=NullContext()):
    if a == b: context.a_equals_b()
    ....

Not more clutter than with only one callback. And the NullContext-object
would actually serve as documentation on what events the code produces.


>> def toplelevel():
>> def callback(a, b):
>> if a == b:
>> raise InterruptException()
>> try:
>> work(callback)
>> except InterruptException:
>> pass
>>
>> def work(callback=callback):
>> a = 10
>> b = 20
>> callback(a, b)
> 
> That's why I said most things that can be more cleanly handled by
> SoftException can usually be handled in other forms, although in a
> more cluttered way.
> 
> That could be more cleanly written in SoftException as:
>     def work():
>         a = 10
>         b = 20
>         raise Equal(a, b)
> 
>     def toplevel():
>         try:
>             work()
>         except Equal, args:
>             a, b = args
>             if a == b:
>                 raise InterruptException

I totally fail to see where 

raise Equal(a, b)

is less cluttered or not than
 
callback(a, b)

Actually, the latter is even less cluttered, misses a raise - if pure number
of literals is your metric, that is.


> If there is a syntax support, you could also make "resume" able to
> transfer values:
> 
>     def somefunc(a, b):
>         if a == b: a, b = raise Equal(a, b)
> 
>     def toplevel():
>         try:
>             somefunc(10, 20)
>         except Equal, args:
>             a, b = args[0], args[1] + 1
>             resume a, b

Sure, you can make all kinds of things, but so far you didn't come up with a
comprehensive feature description that just _does_ specify what SEs are and
what not.

> Perhaps you meant:
>     raise_soft(SoftException)
> 
> cause SoftException() may have side effects if called greedily

Nope, I didn't, and it's beside the point.
 
> That could be done, but when raise_soft() returns, it returns to the
> code that raises it so it must be 'break'en:
> 
>     def caller(a, b):
>         if a == b:
>             raise_soft(SoftException)
>             break
> 
> but this makes SoftException must break everytime, which make it lost
> its original purpose, so you have to add return code to raise_soft
> whether you want to break it or not:

You didn't understand my example. If there is a handler registered, it will
be invoked. If not, nothing will be raised. The exact same amount of
state-keeping and lookups needs to be done by the SE-implementation.

> That could be done, but when raise_soft() returns, it returns to the
> code that raises it so it must be 'break'en:
>     def caller(a, b):
>         if a == b:
>             if raise_soft(SoftException):
>                 break
> 
> Compare to:
>     def caller(a, b):
>         if a == b:
>             raise SoftException
> 
> And this also makes it impossible to have state-changing behavior
> without some other weirder tricks

That's not true. The 

with add_soft_handler(SoftException, handler):

approach (I missed the handrel the first time, sorry)
can easily throw an exception to interrupt, like this:

def handler(e):
    if some_condition_on_e(e):
       raise InterruptException()

with add_soft_handler(SoftException, handler):
     try:
          work(...)
     except InterruptException:
          pass

You could also introduce a function

def interruptable(fun, *args, **kwargs):
    try:
       return fun(*args, **kwargs)
    except InterruptException:
       pass

to make the code look a bit cleaner - if it fits your usecase, that is of
course.

I don't say that SoftExceptions can't have semantics that go beyond this. I
just don't see a oh-so-compelling use-case that makes things so much better
than they are reachable now, without actually much programming.

Diez



More information about the Python-list mailing list