From 1f7d4394d0838847d1963c75b885ee2aece2ff88 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Tue, 20 Jul 2010 13:17:13 +0000 Subject: [PATCH] Make error message slightly more helpful in diagnosing missing angle brackets around an email address in the PEP header (Also added PEP 3150, which was meant to be a separate checkin. C'est la vie) --- pep-3150.txt | 274 +++++++++++++++++++++++++++++++++++++++++++++++++++ pep0/pep.py | 3 +- 2 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 pep-3150.txt diff --git a/pep-3150.txt b/pep-3150.txt new file mode 100644 index 000000000..2841bd7c6 --- /dev/null +++ b/pep-3150.txt @@ -0,0 +1,274 @@ +PEP: 3150 +Title: Statement local namespaces (aka "where" clause) +Version: $Revision$ +Last-Modified: $Date$ +Author: Nick Coghlan +Status: Deferred +Type: Standards Track +Content-Type: text/x-rst +Created: 2010-07-09 +Python-Version: 3.3 +Post-History: 2010-07-14 +Resolution: TBD + + +Abstract +======== + +A recurring proposal on python-ideas is the addition of some form of +statement local namespace. + +This PEP is intended to serve as a focal point for those ideas, so we +can hopefully avoid retreading the same ground a couple of times a +year. Even if the proposal is never accepted having a PEP to point +people to can be valuable (e.g. having PEP 315 helps greatly in avoiding +endless rehashing of loop-and-a-half arguments). + +The ideas in this PEP are just a sketch of a way this concept might work. +They avoid some pitfalls that have been encountered in the past, but +have not themselves been subject to the test of implementation. + + +PEP Deferral +============ + +This PEP is currently deferred at least until the language moratorium +(PEP 3003) is officially lifted by Guido. Even after that, it will +require input from at least the four major Python implementations +(CPython, PyPy, Jython, IronPython) on the feasibility of implementing +the proposed semantics to get it moving again. + +Proposal +======== + +This PEP proposes the addition of an optional "where" clause to the +syntax for simple statements which may contain an expression. The +current list of simple statements that would be affected by this +addition is as follows: + +* expression statement +* assignment statement +* augmented assignment statement +* del statement +* return statement +* yield statement +* raise statement +* assert statement + +The ``where`` clause would allow subexpressions to be referenced by +name in the header line, with the actual definitions following in +the indented clause. As a simple example:: + + c = sqrt(a*a + b*b) where: + a = retrieve_a() + b = retrieve_b() + +Torture Test +============ + +An implementation of this PEP must support execution of the following +code at module, class and function scope:: + + b = {} + a = b[f(a)] = x where: + x = 42 + def f(x): + return x + assert "x" not in locals() + assert "f" not in locals() + assert a == 42 + assert d[42] == 42 where: + d = b + assert "d" not in locals() + +Most naive implementations will choke on the first complex assignment, +while less naive but still broken implementations will fail when +the torture test is executed at class scope. + +And yes, that's a perfectly well-defined assignment statement. Insane, +I agree, but legal:: + + >>> def f(x): return x + ... + >>> x = 42 + >>> b = {} + >>> a = b[f(a)] = x + >>> a + 42 + >>> b + {42: 42} + + +Syntax Change +============= + +Current:: + + expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) + del_stmt: 'del' exprlist + return_stmt: 'return' [testlist] + yield_stmt: yield_expr + raise_stmt: 'raise' [test ['from' test]] + assert_stmt: 'assert' test [',' test] + + +New:: + + expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | + ('=' (yield_expr|testlist_star_expr))*) [where_clause] + del_stmt: 'del' exprlist [where_clause] + return_stmt: 'return' [testlist] [where_clause] + yield_stmt: yield_expr [where_clause] + raise_stmt: 'raise' [test ['from' test]] [where_clause] + assert_stmt: 'assert' test [',' test] [where_clause] + where_clause: "where" ":" suite + +(Note that expr_stmt in the grammar covers assignment and augmented +assignment in addition to simple expression statements) + +The new clause is added as an optional element of the existing statements +rather than as a new kind of compound statement in order to avoid creating +an ambiguity in the grammar. It is applied only to the specific elements +listed so that nonsense like the following is disallowed:: + + pass where: + a = b = 1 + +However, even this is inadequate, as it creates problems for the definition +of simple_stmt (which allows chaining of multiple single line statements +with ";" rather than "\n"). + +So the above syntax change should instead be taken as a statement of intent. +Any actual proposal would need to resolve the simple_stmt parsing problem +before it could be seriously considered. This would likely require a +non-trivial restructuring of the grammar, breaking up small_stmt and +flow_stmt to separate the statements that potentially contain arbitrary +subexpressions and then allowing a single one of those statements with +a ``where`` clause at the simple_stmt level. Something along the lines of:: + + simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE + where_stmt: subexpr_stmt (where_clause | (';' small_stmt)* [';']) NEWLINE + subexpr_stmt: expr_stmt | del_stmt | flow_subexpr_stmt | assert_stmt + small_stmt: (pass_stmt | flow_stmt | import_stmt | + global_stmt | nonlocal_stmt) + flow_stmt: break_stmt | continue_stmt + flow_subexpr_stmt: return_stmt | raise_stmt | yield_stmt + where_clause: "where" ":" suite + + +For reference, here are the current definitions at that level:: + + simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE + small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | + import_stmt | global_stmt | nonlocal_stmt | assert_stmt) + flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt + + +Common Objections +================= + +* Two Ways To Do It: a lot of code may now be written with values + defined either before the expression where they are used or + afterwards in a ``where`` clause, creating two ways to do it, + without an obvious way of choosing between them. + +* Out of Order Execution: the ``where`` clause makes execution + jump around a little strangely, as the body of the ``where`` + clause is executed before the simple statement in the clause + header. The closest any other part of Python comes to this + before is the out of order evaluation in conditional + expressions. + + +Possible Additions +================== + +* The current proposal allows the addition of a ``where`` clause only + for simple statements. Extending the idea to allow the use of + compound statements would be quite possible, but doing so raises + serious readability concerns (as values defined in the ``where`` + clause may be used well before they are defined, exactly the kind + of readability trap that decorators were designed to eliminate) + +* Currently only the outermost clause of comprehensions and generator + expressions can reference the surrounding namespace when executed + at class level. If this proposal is implemented successfully, the + associated namespace semantics could allow that restriction to be + lifted. There would be backwards compatibility implications in doing + so as existing code may be relying on the behaviour of ignoring + class level variables. + +Possible Implementation Strategy +================================ + +AKA How Class Scopes Screw You When Attempting To Implement This + +The natural idea when setting out to implement this concept is to +use an ordinary nested function scope. This doesn't work for the +two reasons mentioned in the Torture Test section above: + +* Non-local variables are not your friend because they ignore class scopes + and (when writing back to the outer scope) aren't really on speaking + terms with module scopes either. + +* Return-based semantics struggle with complex assignment statements + like the one in the torture test + +The most promising approach is one based on symtable analysis and +copy-in-copy-out referencing semantics to move any required name +bindings between the inner and outer scopes. The torture test above +would then translate to something like the following:: + + b = {} + def _anon1(b): # 'b' reference copied in + x = 42 + def f(x): + return x + a = b[f(a)] = x + return a # 'a' reference copied out + a = _anon1(b) + assert "x" not in locals() + assert "f" not in locals() + assert a == 42 + def _anon2(b) # 'b' reference copied in + d = b + assert d[42] == 42 + # Nothing to copy out (not an assignment) + _anon2() + assert "d" not in locals() + +However, as noted in the abstract, an actual implementation of +this idea has never been tried. + + +Reference implementation +======================== + +None as yet. If you want a crash course in Python namespace +semantics and code compilation, feel free to try ;) + + +References +========== + +.. [1] http://mail.python.org/pipermail/python-ideas/2010-June/007476.html + +.. [2] http://mail.python.org/pipermail/python-ideas/2010-July/007584.html + + +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 + End: diff --git a/pep0/pep.py b/pep0/pep.py index 95d54b25e..df4ca9066 100644 --- a/pep0/pep.py +++ b/pep0/pep.py @@ -99,7 +99,8 @@ class Author(object): if part[0].isupper(): break else: - raise ValueError("last name missing a capital letter") + raise ValueError("last name missing a capital letter: %r" + % name_parts) base = u' '.join(name_parts[index:]).lower() return unicodedata.normalize('NFKD', base).encode('ASCII', 'ignore')