[Python-Dev] Multi-line import implementation (was: 2.4a2,
and @decorators)
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