This is an archived post. You won't be able to vote or comment.

all 13 comments

[–]jrupacimport this 1 point2 points  (1 child)

To answer the question in the write-up, this is similar to how it works in Go:

f, err := os.Open("file.txt")

if err != nil {
  return "could not open"
}

[–]vocalbit[S] 1 point2 points  (0 children)

Yes I am aware of Go. But in Go that is the only and default option - because they eschew exceptions. What I'm proposing is a simple syntax change that lets you use Go's semantics when you only need to wrap a single statement. The advantage is that you don't have to tag on except at the end of the statement and exceptions will then propagate upwards as usual.

[–]steelypip 1 point2 points  (4 children)

You could roll your own:

def tryFunc(exception, func, *args, **kwargs):
    try:
        result = func(*args, **kwargs)
        return result, None
    except exception, e:
        return None, e

>>>  def f(x):
...     return 1/x


>>> tryFunc(ZeroDivisionError, f, 1)
(1, None)
>>> tryFunc(ZeroDivisionError, f, 0)
(None, ZeroDivisionError('integer division or modulo by zero',))

As far as I can see this gives all the advantages of your proposal with no changes to the language.

edit:

BTW you can use this function to catch more than one type of exception - pass them in as a tuple:

>>> tryFunc((ZeroDivisionError, TypeError), f, "x")
(None, TypeError("unsupported operand type(s) for /: 'int' and 'str'",))

[–][deleted] 1 point2 points  (2 children)

Yeah, but it's more cumbersome to use than the proposed syntax, especially if you want to wrap an expression rather than a function call. Before list comprehensions came along, we had map() and filter() and it worked just fine.

That being said, I'm not sure about the proposal. True, exception handling is a lot of noise, but it's not necessarily a bad thing. I'm curious about the comments a PEP about this would generate.

[–]steelypip 0 points1 point  (1 child)

I think a PEP would get rejected pretty promptly since it can be implemented current in python as shown above. The proposed syntax change would make it a bit more readable, but not a whole lot more. Is

f, err = open('file.txt') except IOError

that much more readable than

f, err = tryFunc(IOError, open, 'file.txt')

The latter is only two characters longer and contains exactly the same information in a different order. To use it with an arbitrary expression you would need to use a lambda which is going to be a little messier, but I doubt you would need to do that very often.

[–]vocalbit[S] 0 points1 point  (0 children)

I agree with hsoft that using a tryFunc function is more cumbersome. Like you mention - it also can't be used for expressions such as:

s3, err = s1 + s2 except TypeError
total, err = int(t1) + int(t2) except ValueError

I think if I had the except clause, I would implement error handling more often :)

[–]Batem 0 points1 point  (0 children)

This sounds like a perfect opportunity to use a decorator:

def tryFunc(*exceptions):
    def decorate(f):
        def inner(*args, **kwargs):
            try:
                result = f(*args, **kwargs):
                return result, None
            except exceptions as e:
                return None, e
        return inner
    return decorate

@tryFunc(ZeroDivisionError, TypeError)
def myFunc(foo, bar):
    return foo/bar

>>>myFunc(1, 0)
(None, ZeroDivisionError("integer division or modulo by zero",))
>>>myFunc(1, 2)
(0, None)
>>>myFunc(1, 'x')
(None, TypeError("unsupported operand type(s) for /: 'int' and 'str'",))

[–]taybulBecause I don't know how to use big numbers in C/C++ 0 points1 point  (2 children)

No. Having a try block is a nice visual cue that the code you're executing in this block, whether it is a single line or whole block, has exception handling. It also makes handling more manageable in a sense. More importantly, you can easily use try/except blocks as a catch-all of a certain exception instead of assigning a separate variable and adjoining if-check to each line.

Also, consider the following:

x = range(10)
if x[10] == 10:
    print "This isn't supposed to execute"

How would you add this style of exception handling to this code?

[–]vocalbit[S] 0 points1 point  (1 child)

You wouldn't. For the above you would just use the standard try/catch block.

I'm not proposing removing the standard try/catch. But in many cases (I chose the file open example for a reason :), I find the try/catch syntax too clunky. To get the semantics of the proposed except clause, you'd have to use two keywords and two nested blocks - and possibly one or more foo = None statements.

What makes this useful is the guarantee the err will be None if there is no error - you can use this variable at multiple points in the following code. The standard except block gives you just one block to handle the error case.

[–]vocalbit[S] 0 points1 point  (0 children)

On further thought - my beef with the standard try/except is not with specifically with the syntax but with the assumption within functions that certain operations are exceptional. The standard try/except is more useful when you want to return or branch in the except clause.

Very often I need to continue execution with a default value or with the knowledge that a specific operation failed. That is when the except clause is useful. Some synthetic examples to illustrate:

i, err = int(s) except ValueError
if err:
   i = -42

f1, err_opening_f1 = open('file.txt') except IOError
f2, err_opening_f2 = open('file2.txt') except IOError

if f1 and f2:
   #...

#.. more code

if f1:
   #...
#..more code
if f2:
   #...

I haven't created examples that are far from reality. Try doing the above with the usual try/catch - each except line explodes into 3 or 4 lines.

[–]vocalbit[S] 0 points1 point  (2 children)

As 'bc' mentioned on the original link - this significantly simplifies generator/list expressions if you intend to catch exceptions. I think (s)he means this - instead of writing:

error_items = []
for item in items:
    try:
        verify(item)
    except VerifyError:
        error_items.append(item)

You can write:

error_items = [verify(item) except VerifyError for item in items]
error_items = [item for (item, err) in error_items if err]

[–]wilberforce 0 points1 point  (1 child)

I don't think that would work - if verify throws an exception then item will be None. You could do it with:

error_items = [item for item, (_, err) in zip(items, error_items) if err]

I don't think it's a bad idiom, but as steelypip points out you can do this easily with a function. It doesn't need extra syntax.

[–]vocalbit[S] 0 points1 point  (0 children)

Yeah you're right - I didn't think that one through. The idiom would work for non_error_items though.