[Python-Dev] Multi-line import implementation (was: 2.4a2, and @decorators) (original) (raw)

Dima Dorfman dima at trit.org
Mon Aug 9 06:31:05 CEST 2004


Anthony Baxter <anthonyatinterlink.com.au> wrote:

Guido van Rossum wrote: > Did the relative import syntax that was approved ever get checked in? > I thought it was on the list of things to land in 2.4?

Nope. The 2.4 release schedule PEP now lists this as lacking someone to drive it. I won't/can't spend time on it. So, if one of the people who wants it could step forward, that'd be excellent. Even if it's just to do the multi-line import syntax of from foo import ( bar, baz, bar2, baz2, bar3, baz3 )

I took a stab at implementing this (the easy part of PEP 328--parens around import lists). Are the exact semantics of what's allowed documented somewhere? PEP 328 mostly talks about relative and absolute imports, and it doesn't specify the exact semantics of where parentheses should be allowed. My patch (attached) accepts

import (os, sys) from sys import (stdin, stdout, stderr) import (os) from sys import (*)

but rejects

from (sys) import stdin import (os), (sys) import (os,)

Should any of those be allowed? Anything that I missed?

The patch is incomplete in other ways; the docs haven't been updated and neither have the parser module and compile package. If it's decided that it would be okay to include this separately from relative/absolute import support (they're different features, really), I'll complete the patch.

Dima. -------------- next part -------------- Index: Python/compile.c

RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.314 diff -u -c -r2.314 compile.c *** Python/compile.c 6 Aug 2004 19:46:34 -0000 2.314 --- Python/compile.c 9 Aug 2004 04:01:38 -0000 *************** *** 3333,3374 **** static void com_import_stmt(struct compiling c, node n) { int i; REQ(n, import_stmt); ! / 'import' dotted_name (',' dotted_name) | ! 'from' dotted_name 'import' ('' | NAME (',' NAME)) */ ! if (STR(CHILD(n, 0))[0] == 'f') { PyObject tup; - / 'from' dotted_name 'import' ... / REQ(CHILD(n, 1), dotted_name); ! ! if (TYPE(CHILD(n, 3)) == STAR) { tup = Py_BuildValue("(s)", ""); ! } else { ! tup = PyTuple_New((NCH(n) - 2)/2); ! for (i = 3; i < NCH(n); i += 2) { ! PyTuple_SET_ITEM(tup, (i-3)/2, PyString_FromString(STR( ! CHILD(CHILD(n, i), 0)))); } } com_addoparg(c, LOAD_CONST, com_addconst(c, tup)); Py_DECREF(tup); com_push(c, 1); com_addopname(c, IMPORT_NAME, CHILD(n, 1)); ! if (TYPE(CHILD(n, 3)) == STAR) com_addbyte(c, IMPORT_STAR); else { ! for (i = 3; i < NCH(n); i += 2) ! com_from_import(c, CHILD(n, i)); com_addbyte(c, POP_TOP); } com_pop(c, 1); } else { ! /* 'import' ... */ ! for (i = 1; i < NCH(n); i += 2) { ! node *subn = CHILD(n, i); REQ(subn, dotted_as_name); com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); com_push(c, 1); --- 3333,3379 ---- static void com_import_stmt(struct compiling *c, node *n) { + node *nn; int i; REQ(n, import_stmt); ! n = CHILD(n, 0); ! /* import_stmt: just_import | from_import */ ! if (TYPE(n) == from_import) { ! /* from_import: 'from' dotted_name 'import' ('*' | '(' '*' ')' ! | '(' import_as_names ')' | import_as_names) */ PyObject *tup; REQ(CHILD(n, 1), dotted_name); ! nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR)); ! if (TYPE(nn) == STAR) tup = Py_BuildValue("(s)", "*"); ! else { ! REQ(nn, import_as_names); ! tup = PyTuple_New((NCH(nn) + 1) / 2); ! for (i = 0; i < NCH(nn); i += 2) { ! PyTuple_SET_ITEM(tup, i / 2, PyString_FromString(STR( ! CHILD(CHILD(nn, i), 0)))); } } com_addoparg(c, LOAD_CONST, com_addconst(c, tup)); Py_DECREF(tup); com_push(c, 1); com_addopname(c, IMPORT_NAME, CHILD(n, 1)); ! if (TYPE(nn) == STAR) com_addbyte(c, IMPORT_STAR); else { ! for (i = 0; i < NCH(nn); i += 2) ! com_from_import(c, CHILD(nn, i)); com_addbyte(c, POP_TOP); } com_pop(c, 1); } else { ! /* 'import' ('(' dotted_as_names ')' | dotted_as_names)*/ ! nn = CHILD(n, 1 + (TYPE(CHILD(n, 1)) == LPAR)); ! REQ(nn, dotted_as_names); ! for (i = 0; i < NCH(nn); i += 2) { ! node *subn = CHILD(nn, i); REQ(subn, dotted_as_name); com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); com_push(c, 1); *************** *** 6131,6144 **** static void symtable_import(struct symtable *st, node *n) { int i; ! /* import_stmt: 'import' dotted_as_name (',' dotted_as_name)* ! | 'from' dotted_name 'import' ! ('*' | import_as_name (',' import_as_name)*) ! import_as_name: NAME [NAME NAME] ! */ ! if (STR(CHILD(n, 0))[0] == 'f') { /* from */ node *dotname = CHILD(n, 1); if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) { /* check for bogus imports */ if (n->n_lineno >= st->st_future->ff_last_lineno) { --- 6136,6150 ---- static void symtable_import(struct symtable st, node n) { + node nn; int i; ! / import_stmt: just_import | from_import / ! n = CHILD(n, 0); ! if (TYPE(n) == from_import) { ! / from_import: 'from' dotted_name 'import' ('' | '(' '' ')' ! | '(' import_as_names ')' | import_as_names) / node dotname = CHILD(n, 1); + REQ(dotname, dotted_name); if (strcmp(STR(CHILD(dotname, 0)), "future") == 0) { / check for bogus imports / if (n->n_lineno >= st->st_future->ff_last_lineno) { *************** *** 6148,6154 **** return; } } ! if (TYPE(CHILD(n, 3)) == STAR) { if (st->st_cur->ste_type != TYPE_MODULE) { if (symtable_warn(st, "import * only allowed at module level") < 0) --- 6154,6161 ---- return; } } ! nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR)); ! if (TYPE(nn) == STAR) { if (st->st_cur->ste_type != TYPE_MODULE) { if (symtable_warn(st, "import * only allowed at module level") < 0) *************** *** 6157,6164 **** st->st_cur->ste_optimized |= OPT_IMPORT_STAR; st->st_cur->ste_opt_lineno = n->n_lineno; } else { ! for (i = 3; i < NCH(n); i += 2) { ! node *c = CHILD(n, i); if (NCH(c) > 1) / import as / symtable_assign(st, CHILD(c, 2), DEF_IMPORT); --- 6164,6172 ---- st->st_cur->ste_optimized |= OPT_IMPORT_STAR; st->st_cur->ste_opt_lineno = n->n_lineno; } else { ! REQ(nn, import_as_names); ! for (i = 0; i < NCH(nn); i += 2) { ! node *c = CHILD(nn, i); if (NCH(c) > 1) / import as / symtable_assign(st, CHILD(c, 2), DEF_IMPORT); *************** *** 6167,6176 **** DEF_IMPORT); } } ! } else { ! for (i = 1; i < NCH(n); i += 2) { ! symtable_assign(st, CHILD(n, i), DEF_IMPORT); ! } } }
--- 6175,6186 ---- DEF_IMPORT); } } ! } else { ! /
'import' ('(' dotted_as_names ')' | dotted_as_names)
/ ! nn = CHILD(n, 1 + (TYPE(CHILD(n, 1)) == LPAR)); ! REQ(nn, dotted_as_names); ! for (i = 0; i < NCH(nn); i += 2) ! symtable_assign(st, CHILD(nn, i), DEF_IMPORT); } }
Index: Python/future.c

RCS file: /cvsroot/python/python/dist/src/Python/future.c,v retrieving revision 2.13 diff -u -c -r2.13 future.c *** Python/future.c 11 Dec 2002 14:04:59 -0000 2.13 --- Python/future.c 9 Aug 2004 04:01:38 -0000 *************** *** 18,35 **** { int i; char *feature; ! node ch;
! REQ(n, import_stmt); /
must by from future import ... */ ! ! for (i = 3; i < NCH(n); i += 2) { ! ch = CHILD(n, i); ! if (TYPE(ch) == STAR) { ! PyErr_SetString(PyExc_SyntaxError, ! FUTURE_IMPORT_STAR); ! PyErr_SyntaxLocation(filename, ch->n_lineno); ! return -1; ! } REQ(ch, import_as_name); feature = STR(CHILD(ch, 0)); if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) { --- 18,35 ---- { int i; char *feature; ! node *ch, *nn;
! REQ(n, from_import); ! nn = CHILD(n, 3 + (TYPE(CHILD(n, 3)) == LPAR)); ! if (TYPE(nn) == STAR) { ! PyErr_SetString(PyExc_SyntaxError, FUTURE_IMPORT_STAR); ! PyErr_SyntaxLocation(filename, nn->n_lineno); ! return -1; ! } ! REQ(nn, import_as_names); ! for (i = 0; i < NCH(nn); i += 2) { ! ch = CHILD(nn, i); REQ(ch, import_as_name); feature = STR(CHILD(ch, 0)); if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) { *************** *** 188,194 **** case import_stmt: { node *name;
! if (STR(CHILD(n, 0))[0] != 'f') { /* from */ ff->ff_last_lineno = n->n_lineno; return 0; } --- 188,195 ---- case import_stmt: { node *name;
! n = CHILD(n, 0); ! if (TYPE(n) != from_import) { ff->ff_last_lineno = n->n_lineno; return 0; } Index: Grammar/Grammar

RCS file: /cvsroot/python/python/dist/src/Grammar/Grammar,v retrieving revision 1.50 diff -u -c -r1.50 Grammar *** Grammar/Grammar 2 Aug 2004 06:09:53 -0000 1.50 --- Grammar/Grammar 9 Aug 2004 04:01:38 -0000 *************** *** 51,59 **** return_stmt: 'return' [testlist] yield_stmt: 'yield' testlist raise_stmt: 'raise' [test [',' test [',' test]]] ! import_stmt: 'import' dotted_as_name (',' dotted_as_name)* | 'from' dotted_name 'import' ('' | import_as_name (',' import_as_name)) import_as_name: NAME [NAME NAME] dotted_as_name: dotted_name [NAME NAME] dotted_name: NAME ('.' NAME)* global_stmt: 'global' NAME (',' NAME)* exec_stmt: 'exec' expr ['in' test [',' test]] --- 51,63 ---- return_stmt: 'return' [testlist] yield_stmt: 'yield' testlist raise_stmt: 'raise' [test [',' test [',' test]]] ! import_stmt: just_import | from_import ! just_import: 'import' ('(' dotted_as_names ')' | dotted_as_names) ! from_import: 'from' dotted_name 'import' ('' | '(' '' ')' | '(' import_as_names ')' | import_as_names) import_as_name: NAME [NAME NAME] dotted_as_name: dotted_name [NAME NAME] + import_as_names: import_as_name (',' import_as_name)* + dotted_as_names: dotted_as_name (',' dotted_as_name)* dotted_name: NAME ('.' NAME)* global_stmt: 'global' NAME (',' NAME)* exec_stmt: 'exec' expr ['in' test [',' test]] Index: Lib/test/test_compile.py

RCS file: /cvsroot/python/python/dist/src/Lib/test/test_compile.py,v retrieving revision 1.22 diff -u -c -r1.22 test_compile.py *** Lib/test/test_compile.py 2 Aug 2004 08:30:07 -0000 1.22 --- Lib/test/test_compile.py 9 Aug 2004 04:01:39 -0000 *************** *** 211,216 **** --- 211,263 ---- self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'single') self.assertRaises(SyntaxError, compile, stmt, 'tmp', 'exec')
+ def test_import(self): + # These should pass + exec ''' + from future import (nested_scopes, generators) + import sys + import (os, + sys) + from sys import (stdin, stderr, + stdout) + from keyword import (*)''' + # These should fail + try: + exec 'from (sys) import stdin' + self.fail("parens not allowed around single dotted_name") + except SyntaxError: + pass + try: + exec 'import (os), (sys)' + self.fail("entire name list must be enclosed in parens") + except SyntaxError: + pass + try: + exec 'import ((os), (sys))' + self.fail("subparens not allowed") + except SyntaxError: + pass + try: + exec 'import sys)' + self.fail("mismatched parens") + except SyntaxError: + pass + try: + exec 'from sys import stdin)' + self.fail("mismatched parens") + except SyntaxError: + pass + try: + exec 'import (os,)' + self.fail("not a tuple") + except SyntaxError: + pass + try: + exec 'from future import nested_scopes)' + self.fail("mismatched parens in future"); + except SyntaxError: + pass + def test_main(): test_support.run_unittest(TestSpecifics)
Index: Lib/test/test_grammar.py

RCS file: /cvsroot/python/python/dist/src/Lib/test/test_grammar.py,v retrieving revision 1.50 diff -u -c -r1.50 test_grammar.py *** Lib/test/test_grammar.py 19 May 2004 08:20:09 -0000 1.50 --- Lib/test/test_grammar.py 9 Aug 2004 04:01:42 -0000 *************** *** 417,428 **** try: raise KeyboardInterrupt except KeyboardInterrupt: pass
! print 'import_stmt' # 'import' NAME (',' NAME)* | 'from' NAME 'import' ('' | NAME (',' NAME)) import sys import time, sys from time import time from sys import * from sys import path, argv
print 'global_stmt' # 'global' NAME (',' NAME)* def f(): --- 417,434 ---- try: raise KeyboardInterrupt except KeyboardInterrupt: pass
! print 'just_import' # 'import' ('(' dotted_as_names ')' | dotted_as_names) import sys + import (sys) import time, sys + import (time, sys) + print 'from_import' # 'from' dotted_name 'import' ('' | '(' '' ')' | '(' import_as_names ')' | import_as_names) from time import time + from time import (time) from sys import * + from sys import () from sys import path, argv + from sys import (path, argv)
print 'global_stmt' # 'global' NAME (',' NAME)
def f(): Index: Lib/test/output/test_grammar

RCS file: /cvsroot/python/python/dist/src/Lib/test/output/test_grammar,v retrieving revision 1.20 diff -u -c -r1.20 test_grammar *** Lib/test/output/test_grammar 21 May 2003 17:34:49 -0000 1.20 --- Lib/test/output/test_grammar 9 Aug 2004 04:01:42 -0000


*** 35,41 **** testing continue and break in try/except in loop return_stmt raise_stmt ! import_stmt global_stmt exec_stmt assert_stmt --- 35,42 ---- testing continue and break in try/except in loop return_stmt raise_stmt ! just_import ! from_import global_stmt exec_stmt assert_stmt



More information about the Python-Dev mailing list