[Python-Dev] PEP 463: Exception-catching expressions

Thomas Wouters thomas at python.org
Sat Feb 22 11:08:57 CET 2014


On Fri, Feb 21, 2014 at 7:46 PM, Chris Angelico <rosuav at gmail.com> wrote:

> On Sat, Feb 22, 2014 at 9:06 AM, Greg Ewing <greg.ewing at canterbury.ac.nz>
> wrote:
> > Nick Coghlan wrote:
> >>
> >> On 21 February 2014 13:15, Chris Angelico <rosuav at gmail.com> wrote:
> >>
> >>> Generator expressions require parentheses, unless they would be
> >>> strictly redundant.  Ambiguities with except expressions could be
> >>> resolved in the same way, forcing nested except-in-except trees to be
> >>> correctly parenthesized
> >>
> >>
> >> I'd like to make the case that the PEP should adopt this as its
> >> default position.
> >
> >
> > I generally agree, but I'd like to point out that this
> > doesn't necessarily mean making the parenthesizing rules as
> > strict as they are for generator expressions.
> >
> > The starting point for genexps is that the parens are part of
> > the syntax, the same way that square brackets are part of
> > the syntax of a list comprehension; we only allow them to
> > be omitted in very special circumstances.
> >
> > On the other hand, I don't think there's any harm in allowing
> > an except expression to stand on its own when there is no
> > risk of ambiguity, e.g.
> >
> >    foo = things[i] except IndexError: None
> >
> > should be allowed, just as we allow
> >
> >    x = a if b else c
> >
> > and don't require
> >
> >    x = (a if b else c)
>
> I'm inclined to agree with you. I fought against the mandated parens
> for a long time. Would be happy to un-mandate them, as long as there's
> no way for there to be ambiguity. Is CPython able to make an operator
> non-associative? That is, to allow these:
>
> value = expr except Exception: default
> value = (expr except Exception: default) except Exception: default
> value = expr except Exception: (default except Exception: default)
>
> but not this:
>
> value = expr except Exception: default except Exception: default
>
> ?


Yes, although how to do it depends on what else 'default' and 'expr' can
be. The simple answer is to make 'expr' and 'default' be subsets of
expressions that don't include the 'except' expression themselves. Just
like how in 'A if C else B', 'A' and 'C' do not include the 'if' syntax,
but 'B' does: you need parentheses in '(a if c1 else b) if c2 else d' and
'a if (c1 if c3 else c2) else b' but not in 'a if c1 else b if c2 else d'.

However, the question becomes what to do with 'lambda' and 'if-else'. Which
of these should be valid (and mean what it means with the parentheses,
which is how the PEP currently suggests it should be; ones without
parenthesized alternatives are unambiguous, as far as I can tell.)

1. make_adder(x) except TypeError: lambda y: y + 0

2. lambda x: x + 0 except TypeError: 1
-> lambda x: (x + 0 except TypeError: 1)

3. A if f(A) except TypeError: C else B

4. f(A1) except TypeError: A2 if C else B
-> f(A1) except TypeError: (A2 if C else B)

5. f(A1) except TypeError if C else ValueError: f(A2)

6. A if C else f(B) except TypeError: B
-> (A if C else f(B)) except TypeError: B

7. f(A) except E1(A) except E2(A): E2: E1
-> f(A) except (E1(A) except E2(A): E2): E1

8. f(A) or f(B) except TypeError: f(C)
-> (f(A) or f(B)) except TypeError: f(C)

9. f(A) except TypeError: f(B) or f(C)
-> f(A) except TypeError: (f(B) or f(C))

10. A == f(A) except TypeError: B
-> (A == f(A)) except TypeError: B

11. f(A) except TypeError: A == B
-> f(A) except TypeError: (A == B)

12. A + B except TypeError: C
-> (A + B) except TypeError: C

13. f(A) except TypeError: A + B
-> f(A) except TypeError: (A + B)

#6 in particular and to some extent #4, #8 and #10 look wrong to me, so if
they'd be allowed without parentheses perhaps the precedence of if-expr and
except should be more nuanced. ('between lambda and ifexpr' is not really a
sensible way to describe precedence anyway, since despite the insistence of
the reference manual, the precedence of 'lambda' and 'ifexpr' is at best
'mixed': the grammar actually tries to match ifexpr first, but it works the
way we all expect because 'lambda' is not an infix operator.)

It's easy to disallow most of these (allowing only their parenthesized
versions), the question is where to draw the line :) Some of these require
a bit more work in the grammar, duplicating some of the grammar nodes, but
it shouldn't be too bad. However, disallowing any infix operators in the
'default' expression means it'll still be valid, just mean something
different; #9, for example, would be parsed as '(f(A) except TypeError:
f(B)) or f(C)' if the 'default' expression couldn't be an or-expression.
Disallowing it in the 'expr' expression wouldn't have that effect without
parentheses.

(FWIW, I have a working patch without tests that allows all of these, I'll
upload it tonight so people can play with it. Oh, and FWIW, currently I'm
+0 on the idea, -0 on the specific syntax.)
-- 
Thomas Wouters <thomas at python.org>

Hi! I'm an email virus! Think twice before sending your email to help me
spread!
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20140222/24c763c1/attachment.html>


More information about the Python-Dev mailing list