[Python-ideas] except expression

Rob Cliffe rob.cliffe at btinternet.com
Mon Feb 17 07:24:53 CET 2014


On 16/02/2014 04:04, Chris Angelico wrote:
> PEP: XXX
> Title: Exception-catching expressions
> Version: $Revision$
> Last-Modified: $Date$
> Author: Chris Angelico <rosuav at gmail.com>
> Status: Draft
> Type: Standards Track
> Content-Type: text/x-rst
> Created: 15-Feb-2014
> Python-Version: 3.5
> Post-History: 16-Feb-2014
>
>
> Abstract
> ========
>
> Just as PEP 308 introduced a means of value-based conditions in an
> expression, this system allows exception-based conditions to be used as part
> of an expression.
>
>
> Motivation
> ==========
>
> A number of functions and methods have parameters which will cause them to
> return a specified value instead of raising an exception.  The current system
> is ad-hoc and inconsistent, and requires that each function be individually
> written to have this functionality; not all support this.
>
> * dict.get(key, default) - second positional argument in place of KeyError
>
> * next(iter, default) - second positional argument in place of StopIteration
>
> * list.pop() - no way to return a default
>
> (TODO: Get more examples. I know there are some but I can't think of any now.)
>
>
> Rationale
> =========
>
> The current system requires that a function author predict the need for a
> default, and implement support for it. If this is not done, a full try/except
> block is needed.
>
> Note that the specific syntax is open to three metric tons of bike-shedding.
>
> The proposal may well be rejected, but even then is not useless; it can be
> maintained as a collection of failed syntaxes for expression exceptions.
>
>
> Proposal
> ========
>
> Just as the 'or' operator and the three part 'if-else' expression give short
> circuiting methods of catching a falsy value and replacing it, this syntax
> gives a short-circuiting method of catching an exception and replacing it.
>
> This currently works::
>      lst = [1, 2, None, 3]
>      value = lst[2] or "No value"
>
> The proposal adds this::
>      lst = [1, 2]
>      value = lst[2] except IndexError: "No value"
>
> The exception object can be captured just as in a normal try/except block::
>      # Return the next yielded or returned value from a generator
>      value = next(it) except StopIteration as e: e.args[0]
>
> This is effectively equivalent to::
>      try:
>          _ = next(it)
>      except StopIteration as e:
>          _ = e.args[0]
>      value = _
>
> This ternary operator would be between lambda and if/else in precedence.
Oops, I almost missed this (and was going to point out the omission, 
sorry!).
Yes.
So in
         1 / x except ZeroDivisionError: 42
an error will be trapped evaluating "1/x" (not just "x") because "/" has 
higher precedence than "except", as we would all expect.
>
>
> Chaining
> --------
>
> Multiple 'except' keywords can be used, and they will all catch exceptions
> raised in the original expression (only)::
>      value = (expr
>          except Exception1 [as e]: default1
>          except Exception2 [as e]: default2
>          # ... except ExceptionN [as e]: defaultN
>      )
>
> This is not the same as either parenthesized form::
>      value = (("Yield: "+next(it) except StopIteration as e: "End: "+e.args[0])
> except TypeError: "Error: Non-string returned or raised")
>      value = (next(it) except StopIteration as e:
>         (e.args[0] except IndexError: None))
>
> The first form will catch an exception raised in either the original
> expression or in the default expression; the second form will catch ONLY one
> raised by the default expression. All three effects have their uses.
>
>
> Alternative Proposals
> =====================
>
> Discussion on python-ideas brought up the following syntax suggestions::
>      value = expr except default if Exception [as e]
>      value = expr except default for Exception [as e]
>      value = expr except default from Exception [as e]
>      value = expr except Exception [as e] return default
>      value = expr except (Exception [as e]: default)
>      value = expr except Exception [as e] try default
>      value = expr except Exception [as e] continue with default
>      value = default except Exception [as e] else expr
>      value = try expr except Exception [as e]: default
>      value = expr except Exception [as e] pass default
>
> In all cases, default is an expression which will not be evaluated unless
> an exception is raised; if 'as' is used, this expression may refer to the
> exception object.
>
> It has also been suggested that a new keyword be created, rather than reusing
> an existing one. Such proposals fall into the same structure as the last
> form, but with a different keyword in place of 'pass'. Suggestions include
> 'then', 'when', and 'use'.
>
>
> Open Issues
> ===========
>
> finally clause
> --------------
>
> Should the except expression be able to have a finally clause? No form of the
> proposal so far has included finally.
I don't see (though I could be missing something) that "finally" makes 
any sense inside an expression.  "finally" introduces an *action* to be 
performed regardless of whether a preceding operation raised an error.  
Here we are evaluating an *expression*, and to tack on "I want to 
perform this action" on the end of an expression seems weird (and better 
done by the existing try ... finally mechanism). It did occur to me 
briefly that it might be used to provide a final default:
     x = eval(UserExpression) except NameError: "You forgot to define 
something" except ZeroDivisionError: "Division by zero" finally: "Some 
error or other"
but this would be pretty well equivalent to using a bare "except:" 
clause, or "except BaseException".

Oh yes, by the way, I think that a bare "except:" should be legal (for 
consistency), albeit discouraged.

>
>
> Commas between multiple except clauses
> --------------------------------------
> Where there are multiple except clauses, should they be separated by commas?
> It may be easier for the parser, that way::
>      value = (expr
>          except Exception1 [as e]: default1,
>          except Exception2 [as e]: default2,
>          # ... except ExceptionN [as e]: defaultN,
>      )
> with an optional comma after the last, as per tuple rules. Downside: Omitting
> the comma would be syntactically valid, and would have almost identical
> semantics, but would nest the entire preceding expression in its exception
> catching rig - a matching exception raised in the default clause would be
> caught by the subsequent except clause. As this difference is so subtle, it
> runs the risk of being a major bug magnet.

I would argue against comma separators being allowed:
     1) They add extra grit to the syntax.
     2) When the parser sees a comma, it doesn't know if this is 
terminating the except clause, or part of a tuple default value:
         value = expr except Exception1: x,y, except Exception2: z
         (The ambiguity is resolved when it sees another "except". The 
intervening comma just adds confusion and makes the parser's job *harder*.)
        And for what it's worth, if you did want a 1-element tuple as 
your default value you might write the ugly
        value = expr except Exception1: x,, except Exception2: z   # 
Yuk!  Please put "x," inside parentheses.
     3) With "no commas", the subtly different semantics you mention 
would be distinguished transparently by
         value = expr except Exception1: default1 except Exception2: 
default2
         value = (expr except Exception1: default1) except Exception2: 
default2
In the first form, Exception2 will not be caught if it is raised 
evaluating default1.  In the second it will be.

Rob Cliffe
>
>
> Copyright
> =========
>
> This document has been placed in the public domain.
>
>
>
>
> ..
>     Local Variables:
>     mode: indented-text
>     indent-tabs-mode: nil
>     sentence-end-double-space: t
>     fill-column: 70
>     coding: utf-8
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
>
> -----
> No virus found in this message.
> Checked by AVG - www.avg.com
> Version: 2012.0.2247 / Virus Database: 3705/6597 - Release Date: 02/16/14
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20140217/91cc27a0/attachment-0001.html>


More information about the Python-ideas mailing list