Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 81e9502

Browse files
committed
Provisional implementation of PEP 3104.
Add nonlocal_stmt to Grammar and Nonlocal node to AST. They both parallel the definitions for globals. The symbol table treats variables declared as nonlocal just like variables that are free implicitly. This change is missing the language spec changes, but makes some decisions about what the spec should say via the unittests. The PEP is silent on a number of decisions, so we should review those before claiming that nonlocal is complete. Thomas Wouters made the grammer and ast changes. Jeremy Hylton added the symbol table changes and the tests. Pete Shinners and Neal Norwitz helped review the code.
1 parent 8b41c3d commit 81e9502

12 files changed

Lines changed: 1130 additions & 864 deletions

File tree

Grammar/Grammar

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ vfplist: vfpdef (',' vfpdef)* [',']
3939
stmt: simple_stmt | compound_stmt
4040
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
4141
small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt |
42-
import_stmt | global_stmt | assert_stmt)
42+
import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
4343
expr_stmt: testlist (augassign (yield_expr|testlist) |
4444
('=' (yield_expr|testlist))*)
4545
augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
@@ -63,6 +63,7 @@ import_as_names: import_as_name (',' import_as_name)* [',']
6363
dotted_as_names: dotted_as_name (',' dotted_as_name)*
6464
dotted_name: NAME ('.' NAME)*
6565
global_stmt: 'global' NAME (',' NAME)*
66+
nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
6667
assert_stmt: 'assert' test [',' test]
6768

6869
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef

Include/Python-ast.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ enum _stmt_kind {FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3,
6666
While_kind=8, If_kind=9, With_kind=10, Raise_kind=11,
6767
TryExcept_kind=12, TryFinally_kind=13, Assert_kind=14,
6868
Import_kind=15, ImportFrom_kind=16, Global_kind=17,
69-
Expr_kind=18, Pass_kind=19, Break_kind=20, Continue_kind=21};
69+
Nonlocal_kind=18, Expr_kind=19, Pass_kind=20, Break_kind=21,
70+
Continue_kind=22};
7071
struct _stmt {
7172
enum _stmt_kind kind;
7273
union {
@@ -164,6 +165,10 @@ struct _stmt {
164165
asdl_seq *names;
165166
} Global;
166167

168+
struct {
169+
asdl_seq *names;
170+
} Nonlocal;
171+
167172
struct {
168173
expr_ty value;
169174
} Expr;
@@ -422,6 +427,9 @@ stmt_ty _Py_ImportFrom(identifier module, asdl_seq * names, int level, int
422427
#define Global(a0, a1, a2, a3) _Py_Global(a0, a1, a2, a3)
423428
stmt_ty _Py_Global(asdl_seq * names, int lineno, int col_offset, PyArena
424429
*arena);
430+
#define Nonlocal(a0, a1, a2, a3) _Py_Nonlocal(a0, a1, a2, a3)
431+
stmt_ty _Py_Nonlocal(asdl_seq * names, int lineno, int col_offset, PyArena
432+
*arena);
425433
#define Expr(a0, a1, a2, a3) _Py_Expr(a0, a1, a2, a3)
426434
stmt_ty _Py_Expr(expr_ty value, int lineno, int col_offset, PyArena *arena);
427435
#define Pass(a0, a1, a2) _Py_Pass(a0, a1, a2)

Include/graminit.h

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -35,53 +35,54 @@
3535
#define dotted_as_names 290
3636
#define dotted_name 291
3737
#define global_stmt 292
38-
#define assert_stmt 293
39-
#define compound_stmt 294
40-
#define if_stmt 295
41-
#define while_stmt 296
42-
#define for_stmt 297
43-
#define try_stmt 298
44-
#define with_stmt 299
45-
#define with_var 300
46-
#define except_clause 301
47-
#define suite 302
48-
#define testlist_safe 303
49-
#define old_test 304
50-
#define old_lambdef 305
51-
#define test 306
52-
#define or_test 307
53-
#define and_test 308
54-
#define not_test 309
55-
#define comparison 310
56-
#define comp_op 311
57-
#define expr 312
58-
#define xor_expr 313
59-
#define and_expr 314
60-
#define shift_expr 315
61-
#define arith_expr 316
62-
#define term 317
63-
#define factor 318
64-
#define power 319
65-
#define atom 320
66-
#define listmaker 321
67-
#define testlist_gexp 322
68-
#define lambdef 323
69-
#define trailer 324
70-
#define subscriptlist 325
71-
#define subscript 326
72-
#define sliceop 327
73-
#define exprlist 328
74-
#define testlist 329
75-
#define dictsetmaker 330
76-
#define classdef 331
77-
#define arglist 332
78-
#define argument 333
79-
#define list_iter 334
80-
#define list_for 335
81-
#define list_if 336
82-
#define gen_iter 337
83-
#define gen_for 338
84-
#define gen_if 339
85-
#define testlist1 340
86-
#define encoding_decl 341
87-
#define yield_expr 342
38+
#define nonlocal_stmt 293
39+
#define assert_stmt 294
40+
#define compound_stmt 295
41+
#define if_stmt 296
42+
#define while_stmt 297
43+
#define for_stmt 298
44+
#define try_stmt 299
45+
#define with_stmt 300
46+
#define with_var 301
47+
#define except_clause 302
48+
#define suite 303
49+
#define testlist_safe 304
50+
#define old_test 305
51+
#define old_lambdef 306
52+
#define test 307
53+
#define or_test 308
54+
#define and_test 309
55+
#define not_test 310
56+
#define comparison 311
57+
#define comp_op 312
58+
#define expr 313
59+
#define xor_expr 314
60+
#define and_expr 315
61+
#define shift_expr 316
62+
#define arith_expr 317
63+
#define term 318
64+
#define factor 319
65+
#define power 320
66+
#define atom 321
67+
#define listmaker 322
68+
#define testlist_gexp 323
69+
#define lambdef 324
70+
#define trailer 325
71+
#define subscriptlist 326
72+
#define subscript 327
73+
#define sliceop 328
74+
#define exprlist 329
75+
#define testlist 330
76+
#define dictsetmaker 331
77+
#define classdef 332
78+
#define arglist 333
79+
#define argument 334
80+
#define list_iter 335
81+
#define list_for 336
82+
#define list_if 337
83+
#define gen_iter 338
84+
#define gen_for 339
85+
#define gen_if 340
86+
#define testlist1 341
87+
#define encoding_decl 342
88+
#define yield_expr 343

Include/symtable.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,24 @@ PyAPI_FUNC(void) PySymtable_Free(struct symtable *);
6464
#define DEF_GLOBAL 1 /* global stmt */
6565
#define DEF_LOCAL 2 /* assignment in code block */
6666
#define DEF_PARAM 2<<1 /* formal parameter */
67-
#define USE 2<<2 /* name is used */
68-
#define DEF_STAR 2<<3 /* parameter is star arg */
69-
#define DEF_DOUBLESTAR 2<<4 /* parameter is star-star arg */
70-
#define DEF_INTUPLE 2<<5 /* name defined in tuple in parameters */
71-
#define DEF_FREE 2<<6 /* name used but not defined in nested block */
72-
#define DEF_FREE_GLOBAL 2<<7 /* free variable is actually implicit global */
73-
#define DEF_FREE_CLASS 2<<8 /* free variable from class's method */
74-
#define DEF_IMPORT 2<<9 /* assignment occurred via import */
67+
#define DEF_NONLOCAL 2<<2 /* nonlocal stmt */
68+
#define USE 2<<3 /* name is used */
69+
#define DEF_STAR 2<<4 /* parameter is star arg */
70+
#define DEF_DOUBLESTAR 2<<5 /* parameter is star-star arg */
71+
#define DEF_INTUPLE 2<<6 /* name defined in tuple in parameters */
72+
#define DEF_FREE 2<<7 /* name used but not defined in nested block */
73+
#define DEF_FREE_GLOBAL 2<<8 /* free variable is actually implicit global */
74+
#define DEF_FREE_CLASS 2<<9 /* free variable from class's method */
75+
#define DEF_IMPORT 2<<10 /* assignment occurred via import */
7576

7677
#define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT)
7778

7879
/* GLOBAL_EXPLICIT and GLOBAL_IMPLICIT are used internally by the symbol
7980
table. GLOBAL is returned from PyST_GetScope() for either of them.
80-
It is stored in ste_symbols at bits 12-14.
81+
It is stored in ste_symbols at bits 12-15.
8182
*/
8283
#define SCOPE_OFF 11
83-
#define SCOPE_MASK 7
84+
#define SCOPE_MASK (DEF_GLOBAL | DEF_LOCAL | DEF_PARAM | DEF_NONLOCAL)
8485

8586
#define LOCAL 1
8687
#define GLOBAL_EXPLICIT 2

Lib/test/test_scope.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,90 @@ def g():
555555

556556
f(4)()
557557

558+
def testNonLocalFunction(self):
559+
560+
def f(x):
561+
def inc():
562+
nonlocal x
563+
x += 1
564+
return x
565+
def dec():
566+
nonlocal x
567+
x -= 1
568+
return x
569+
return inc, dec
570+
571+
inc, dec = f(0)
572+
self.assertEqual(inc(), 1)
573+
self.assertEqual(inc(), 2)
574+
self.assertEqual(dec(), 1)
575+
self.assertEqual(dec(), 0)
576+
577+
def testNonLocalMethod(self):
578+
579+
def f(x):
580+
class c:
581+
def inc(self):
582+
nonlocal x
583+
x += 1
584+
return x
585+
def dec(self):
586+
nonlocal x
587+
x -= 1
588+
return x
589+
return c()
590+
591+
c = f(0)
592+
self.assertEqual(c.inc(), 1)
593+
self.assertEqual(c.inc(), 2)
594+
self.assertEqual(c.dec(), 1)
595+
self.assertEqual(c.dec(), 0)
596+
597+
def testNonLocalClass(self):
598+
599+
def f(x):
600+
class c:
601+
nonlocal x
602+
x += 1
603+
def get(self):
604+
return x
605+
return c()
606+
607+
c = f(0)
608+
self.assertEqual(c.get(), 1)
609+
self.assert_("x" not in c.__class__.__dict__)
610+
611+
612+
def testNonLocalGenerator(self):
613+
614+
def f(x):
615+
def g(y):
616+
nonlocal x
617+
for i in range(y):
618+
x += 1
619+
yield x
620+
return g
621+
622+
g = f(0)
623+
self.assertEqual(list(g(5)), [1, 2, 3, 4, 5])
624+
625+
def testNestedNonLocal(self):
626+
627+
def f(x):
628+
def g():
629+
nonlocal x
630+
x -= 2
631+
def h():
632+
nonlocal x
633+
x += 4
634+
return x
635+
return h
636+
return g
637+
638+
g = f(1)
639+
h = g()
640+
self.assertEqual(h(), 3)
641+
558642

559643
def test_main():
560644
run_unittest(ScopeTests)

Lib/test/test_syntax.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,46 @@
367367
...
368368
SystemError: too many statically nested blocks
369369
370+
Misuse of the nonlocal statement can lead to a few unique syntax errors.
371+
372+
>>> def f(x):
373+
... nonlocal x
374+
Traceback (most recent call last):
375+
...
376+
SyntaxError: name 'x' is local and nonlocal
377+
378+
>>> def f():
379+
... global x
380+
... nonlocal x
381+
Traceback (most recent call last):
382+
...
383+
SyntaxError: name 'x' is nonlocal and global
384+
385+
>>> def f():
386+
... nonlocal x
387+
Traceback (most recent call last):
388+
...
389+
SyntaxError: no binding for nonlocal 'x' found
390+
391+
TODO(jhylton): Figure out how to test SyntaxWarning with doctest.
392+
393+
## >>> def f(x):
394+
## ... def f():
395+
## ... print(x)
396+
## ... nonlocal x
397+
## Traceback (most recent call last):
398+
## ...
399+
## SyntaxWarning: name 'x' is assigned to before nonlocal declaration
400+
401+
## >>> def f():
402+
## ... x = 1
403+
## ... nonlocal x
404+
## Traceback (most recent call last):
405+
## ...
406+
## SyntaxWarning: name 'x' is assigned to before nonlocal declaration
407+
408+
409+
370410
"""
371411

372412
import re

Parser/Python.asdl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ module Python version "$Revision$"
3434
| ImportFrom(identifier module, alias* names, int? level)
3535

3636
| Global(identifier* names)
37+
| Nonlocal(identifier* names)
3738
| Expr(expr value)
3839
| Pass | Break | Continue
3940

Python/Python-ast.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ static PyTypeObject *Global_type;
131131
static char *Global_fields[]={
132132
"names",
133133
};
134+
static PyTypeObject *Nonlocal_type;
135+
static char *Nonlocal_fields[]={
136+
"names",
137+
};
134138
static PyTypeObject *Expr_type;
135139
static char *Expr_fields[]={
136140
"value",
@@ -507,6 +511,8 @@ static int init_types(void)
507511
if (!ImportFrom_type) return 0;
508512
Global_type = make_type("Global", stmt_type, Global_fields, 1);
509513
if (!Global_type) return 0;
514+
Nonlocal_type = make_type("Nonlocal", stmt_type, Nonlocal_fields, 1);
515+
if (!Nonlocal_type) return 0;
510516
Expr_type = make_type("Expr", stmt_type, Expr_fields, 1);
511517
if (!Expr_type) return 0;
512518
Pass_type = make_type("Pass", stmt_type, NULL, 0);
@@ -1145,6 +1151,20 @@ Global(asdl_seq * names, int lineno, int col_offset, PyArena *arena)
11451151
return p;
11461152
}
11471153

1154+
stmt_ty
1155+
Nonlocal(asdl_seq * names, int lineno, int col_offset, PyArena *arena)
1156+
{
1157+
stmt_ty p;
1158+
p = (stmt_ty)PyArena_Malloc(arena, sizeof(*p));
1159+
if (!p)
1160+
return NULL;
1161+
p->kind = Nonlocal_kind;
1162+
p->v.Nonlocal.names = names;
1163+
p->lineno = lineno;
1164+
p->col_offset = col_offset;
1165+
return p;
1166+
}
1167+
11481168
stmt_ty
11491169
Expr(expr_ty value, int lineno, int col_offset, PyArena *arena)
11501170
{
@@ -2197,6 +2217,15 @@ ast2obj_stmt(void* _o)
21972217
goto failed;
21982218
Py_DECREF(value);
21992219
break;
2220+
case Nonlocal_kind:
2221+
result = PyType_GenericNew(Nonlocal_type, NULL, NULL);
2222+
if (!result) goto failed;
2223+
value = ast2obj_list(o->v.Nonlocal.names, ast2obj_identifier);
2224+
if (!value) goto failed;
2225+
if (PyObject_SetAttrString(result, "names", value) == -1)
2226+
goto failed;
2227+
Py_DECREF(value);
2228+
break;
22002229
case Expr_kind:
22012230
result = PyType_GenericNew(Expr_type, NULL, NULL);
22022231
if (!result) goto failed;
@@ -3049,6 +3078,8 @@ init_ast(void)
30493078
0) return;
30503079
if (PyDict_SetItemString(d, "Global", (PyObject*)Global_type) < 0)
30513080
return;
3081+
if (PyDict_SetItemString(d, "Nonlocal", (PyObject*)Nonlocal_type) < 0)
3082+
return;
30523083
if (PyDict_SetItemString(d, "Expr", (PyObject*)Expr_type) < 0) return;
30533084
if (PyDict_SetItemString(d, "Pass", (PyObject*)Pass_type) < 0) return;
30543085
if (PyDict_SetItemString(d, "Break", (PyObject*)Break_type) < 0) return;

0 commit comments

Comments
 (0)